Shell Loops That Actually Work: for, while, until, and When to Use Each
Getting loops right is the difference between a script that works on your machine and one that survives filenames with spaces, runs on stripped-down containers, and doesn't spin a CPU core to 100%. The while read vs. for-over-find distinction alone prevents a whole class of production bugs.
Shell scripting's real power sits in its three loop structures: for, while, and until. for iterates over a known list — numbers, files, command output, or array elements — and is the go-to when the iteration count is fixed. while runs as long as a condition holds true, making it the right choice for reading files line-by-line, polling services, or any scenario where the endpoint isn't known upfront. until inverts the logic, looping while a condition is false, which reads more naturally for wait-until-ready patterns.
Practical details separate working scripts from broken ones. Brace expansion ({1..5}) is clean but fails in dash or busybox; seq handles negative steps and zero-padding portably. while read avoids the whitespace-splitting bugs that plague for loops over find output. break and continue control flow, and break n can exit multiple nested levels at once.
Five worked examples — summing 1–100, a multiplication table, service health polling with a timeout, batch file renaming, and CSV statistics — show how these loops compose into real automation. The patterns are small but form the backbone of most ops and data-processing scripts.
Shell's loop constructs map cleanly to intent: for when you know the set, while when you know the condition, until when the exit condition is clearer than the continue condition. Most scripting languages blur these, but shell keeps them distinct, which rewards picking the right one.
The while read pattern is shell's most underappreciated safety mechanism. It sidesteps word-splitting entirely, making it the only reliable way to process arbitrary filenames or CSV rows in production scripts.
Brace expansion's portability trap is a quiet source of breakage when scripts move from interactive bash to container entrypoints running dash or busybox. seq is the safer default for any script that might leave a developer's laptop.