Use find instead of ls to iterate files

Parsing `ls` output breaks on filenames with spaces, newlines, or quotes. Use `find` with NUL delimiters.

Problem

`ls` is for humans. Its output format is locale-dependent and unstable, and a filename can contain any byte except NUL — including newlines. Scripts that loop over `ls` output silently corrupt on the first weird filename.

Bad

for f in $(ls *.log); do
  process "$f"
done

Good

# Globbing inside the shell — safe for spaces:
shopt -s nullglob
for f in *.log; do
  process "$f"
done

# Or NUL-delimited with find for recursive cases:
find . -name '*.log' -print0 | while IFS= read -r -d '' f; do
  process "$f"
done

Explanation

The shell's own globbing handles spaces correctly. `nullglob` makes a no-match glob expand to nothing instead of staying literal. For recursive traversals, `find -print0` plus `read -d ''` is the only fully safe pattern.

Related

SC2045

SC2086

When It Matters

ls formats output for humans: it may add colour escape codes, replace unprintable characters with question marks, quote names containing spaces, and change column layout depending on whether output is a terminal. Parsing it means your script depends on all of that, and none of it is stable. Filenames containing newlines break line-based parsing of ls entirely, and unlike most theoretical concerns, they arrive routinely from extracted archives and user uploads.

Second Example

Note

nullglob matters: without it, a glob that matches nothing is passed through literally and the loop runs once with the pattern as the value.

Exceptions

ls is the right tool when a human is going to read the output, and ls -t or ls -S with a careful NUL-free assumption is still occasionally the shortest route to a sorted list. Where correctness matters, find -printf or stat gives the same data in a parseable form.

Faq

Q

How do I sort files by modification time without ls?

A

find . -maxdepth 1 -printf '%T@ %p\0' | sort -zn gives time-sorted, NUL-delimited output that survives any filename.

Q

What does nullglob do?

A

It makes an unmatched glob expand to nothing rather than to the literal pattern, which stops loops running once with a bogus value.

Q

How do I count files in a directory?

A

Collect them into an array with a glob and read ${#arr[@]}, or use find ... -printf . | wc -c. Counting lines of ls is wrong for names with newlines.