Proper Iteration in Shell: Avoiding Word Splitting in For Loops

Learn safe patterns for iterating over files and data in bash. Avoid common for-loop pitfalls with word splitting and glob expansion.

Quoting

Detailed Explanation

Safe Iteration Patterns in Shell Scripts

For loops are one of the most misused constructs in shell scripting. Combining them with command substitution or unquoted variables leads to subtle bugs with filenames containing spaces or special characters.

The Anti-Pattern

# BAD: Breaks on filenames with spaces
for f in $(ls /path/to/dir); do
  process "$f"
done

If a file is named "my file.txt", this loop processes "my" and "file.txt" as separate items.

Safe Alternatives

Glob patterns (preferred for simple cases):

for f in /path/to/dir/*; do
  [ -e "$f" ] || continue  # Skip if no matches
  process "$f"
done

find with while-read (for recursive searches):

while IFS= read -r -d '' file; do
  process "$file"
done < <(find /path -name "*.txt" -print0)

The -print0 and -d '' combination handles all possible filenames, including those with newlines.

Iterating Over Lines

# BAD: word splitting on spaces within lines
for line in $(cat file.txt); do
  echo "$line"
done

# GOOD: read line by line
while IFS= read -r line; do
  echo "$line"
done < file.txt

Iterating Over Arrays

files=("file one.txt" "file two.txt" "file three.txt")

# GOOD: iterate with quoted expansion
for f in "${files[@]}"; do
  echo "$f"
done

The key is always quoting "${array[@]}" to preserve element boundaries.

Use Case

Any shell script that processes multiple files, reads lists of items, or iterates over directory contents. Backup scripts, batch processing tools, and file management automation all benefit from safe iteration patterns.

Try It — Shell Script Linter

Open full tool