Terminal · intermediate

Shell loops

Shell for, while, and until constructs repeatedly execute command lists over an argument sequence or while a status condition holds.

Why it matters

Loops automate repeated work, but input parsing and subprocess placement determine whether pathnames, whitespace, and accumulated state remain correct.

Mental model

How to reason about shell loops

Choose an iteration source with explicit record boundaries, bind one logical item, run the body, and use the body's commands and status to continue or stop.

Analogy

A loop is an assembly station receiving one sealed item at a time; reliability depends on the conveyor preserving where each item begins and ends.

Examples

See the boundary, not just the happy path

Worked example · Iterate over script arguments

for path in "$@"; do process "$path"; done

Quoted $@ expands to one word per original argument, and quoting $path preserves that boundary when passed onward.

Worked example · Read lines without backslash processing

while IFS= read -r line || [ -n "$line" ]; do printf '%s\n' "$line"; done < input.txt

IFS= preserves edge whitespace, -r preserves backslashes, and the second condition processes a final nonempty record even when the file lacks a trailing newline.

Avoid · Splitting command output

for file in $(find . -type f); do process "$file"; done

The substitution is split and globbed, so legal filenames can become multiple iterations or expand to unrelated paths.

Common mistakes

Misconceptions to remove early

Using for line in $(cat file)

This iterates over shell-split words rather than lines and also performs filename expansion; use read with explicit input semantics.

Expecting pipeline-loop assignments to persist

Many shells execute pipeline components in subshell environments, so variables changed inside producer | while ... may not survive after the loop.

Quick check

Can you predict the result?

1. Which loop header preserves each original script argument as one iteration?
  • for arg in "$@"
  • for arg in $*
  • for arg in $(printf '%s' "$@")
Answer: for arg in "$@"
2. Why use IFS= read -r for general line input?
Answer: It avoids trimming IFS whitespace and prevents backslash escape processing.

Keep building

Authoritative references

Make the idea retrievable.

Study this concept with spaced repetition, next to the commands where you use it.

Get Terminaster