Loop over files safely when names may contain spaces or newlines
A cleanup job compresses every file under ./reports, but the loop below mangles paths that contain a space — Q3 report.csv is split into Q3 and report.csv, and each half fails. Names may also contain tabs or newlines.
Constraints: do not parse ls; the loop must handle any filename byte except NUL. Iterate so each path is passed to gzip intact.
for f in $(find ./reports -type f); do
gzip "$f"
done
Rewrite the loop to iterate safely.
for f in $(find …) is unsafe: the substitution is word-split on IFS, so a path with a space or newline breaks into several iterations and a glob in a name expands. Iterate on NUL-delimited output instead: find … -print0 | while IFS= read -r -d '' f; do …; done. The \0 cannot occur in a filename, and IFS=/-r keep each name literal.
- ✗Iterating
for f in $(find …)or$(ls), which word-splits on every space - ✗Quoting the whole substitution, which then yields one giant single value
- ✗Dropping
IFS=or-r, soreadtrims whitespace or eats backslashes
- →Why is
-print0paired withread -d ''instead of a newline delimiter? - →How would
find -exec gzip {} +compare with thewhile readloop here?
Solution
The bug is that $(find …) undergoes word-splitting on IFS (space, tab, newline) before the value ever reaches f, so Q3 report.csv becomes two iterations. Quoting "$f" inside the body cannot help — the split already happened at substitution time. A glob-like name such as *.csv would expand too.
The robust form iterates on a NUL (\0) delimiter, the one byte that can never appear in a filename:
find ./reports -type f -print0 | while IFS= read -r -d '' f; do
gzip "$f"
done
stops \ from being treated as an escape.
-print0—findseparates paths with\0instead of a newline.read -d ''— reads up to a\0;IFS=disables whitespace trimming;-r"$f"— quoted when passed togzip.
A loop-free alternative is find ./reports -type f -exec gzip {} +, also space-safe. Never parse ls to drive iteration.