gotchabashMajor
Avoid parsing ls output: use glob or find instead
Viewed 0 times
lsglobfindparse outputfilenamesword splittingnullglobantipattern
Problem
Developers pipe 'ls' output to process files: 'for f in $(ls *.txt)'. This breaks on filenames with spaces, newlines, or special characters because ls output is text meant for humans, not machines.
Solution
Use glob expansion directly or find with -print0 / null delimiters.
# NEVER parse ls output
for f in $(ls *.txt); do ... # WRONG
# Direct glob: safe, handles spaces
for f in *.txt; do
echo "Processing: $f"
done
# If no files match, handle empty glob
shopt -s nullglob
files=(*.txt)
(( ${#files[@]} > 0 )) || { echo 'No files'; exit; }
for f in "${files[@]}"; do ...
# For complex criteria, use find:
find . -maxdepth 1 -name '*.txt' -print0 \
| xargs -0 -I{} process {}
# NEVER parse ls output
for f in $(ls *.txt); do ... # WRONG
# Direct glob: safe, handles spaces
for f in *.txt; do
echo "Processing: $f"
done
# If no files match, handle empty glob
shopt -s nullglob
files=(*.txt)
(( ${#files[@]} > 0 )) || { echo 'No files'; exit; }
for f in "${files[@]}"; do ...
# For complex criteria, use find:
find . -maxdepth 1 -name '*.txt' -print0 \
| xargs -0 -I{} process {}
Why
ls formats its output for human readability (columns, colors, quoting). It is not designed as a data source. Filename characters like spaces, newlines, and special characters corrupt the output when parsed as a list.
Gotchas
- shopt -s nullglob makes non-matching globs expand to nothing (empty list) instead of the literal pattern
- shopt -s failglob makes non-matching globs an error — useful in strict scripts
- Glob patterns are sorted by default; find order is filesystem-dependent
- ls -1 (one per line) still breaks on filenames with newlines
- ls output differs between macOS and Linux (BSD vs GNU ls)
Code Snippets
Safe file iteration without parsing ls
# WRONG: parsing ls output
for f in $(ls /tmp/*.log); do
echo "$f" # breaks on spaces in filenames
done
# RIGHT: direct glob
shopt -s nullglob
for f in /tmp/*.log; do
echo "$f" # safe for all valid filenames
done
# RIGHT: array of matches
shopt -s nullglob
files=(/tmp/*.log)
if (( ${#files[@]} == 0 )); then
echo "No log files found"
exit 0
fi
for f in "${files[@]}"; do process "$f"; doneContext
Iterating over files in a directory in bash scripts
Revisions (0)
No revisions yet.