SC2044 — Stop looping over find output: safe fix
Free fix for ShellCheck SC2044 with a working example. `for f in $(find ...)` breaks on any filename with a space. Paste your script for an instant analysis.
Problem
`for f in $(find ...)` word-splits the entire output on whitespace. Any filename with a space becomes multiple loop iterations, and any filename with a glob character is expanded again.
Bad
for f in $(find . -name '*.log'); do
gzip "$f"
doneGood
while IFS= read -r -d '' f; do
gzip "$f"
done < <(find . -name '*.log' -print0)Explanation
The NUL-delimited pattern is the only safe way to iterate over arbitrary filenames in Bash. Alternatively, push the work into `find -exec`.
Related
SC2038
When It Matters
for f in $(find ...) splits find’s output on whitespace and globs each fragment, so any path containing a space is processed as two nonexistent paths and any path containing a metacharacter is expanded against the working directory. Directory trees produced by other people — archives, uploads, macOS folders — routinely contain spaces. It also buffers the whole result before the first iteration, which matters on large trees.
Second Example
Note
Piping into the loop puts it in a subshell; process substitution with < <(...) keeps it in the current shell so counters and arrays persist.
Exceptions
For a shallow, non-recursive case a glob is simpler than find and already safe: for f in ./*.conf. Reach for find when you need recursion or conditions on time, size, or type.
Faq
Q
Why does my counter reset to zero after the loop?
A
The loop ran in a subshell because it was on the right side of a pipe. Use process substitution instead.
Q
Is -print0 portable?
A
It is a GNU and BSD extension, available on Linux and macOS. In strictly POSIX environments, use -exec with a small helper script.
Q
What does read -d '' do?
A
It sets the delimiter to NUL, which is the one byte that cannot appear in a filename — making the split unambiguous.