Bash for loop — complete guide
Learn Bash for loops: iterate over lists, ranges, files, arrays, and command output. Copy-paste examples that handle spaces and globs safely.
Tldr
Use `for item in list; do ... done` to iterate. Quote the variable inside the body. For numeric ranges use `{1..5}` or `seq`. For files use globs directly, not `$(ls)`. For arrays use `"${arr[@]}"`.
Intro
The Bash for loop is the workhorse of shell scripting. The right pattern depends on what you are iterating over: a fixed list, a numeric range, files, or array elements. This guide covers each pattern with safe, copy-paste examples.
Steps
Name
Loop over a fixed list
Text
The classic form assigns each word in the list to the loop variable in turn. Quote the variable in the body to preserve values with spaces.
Name
Loop over a numeric range
Text
Brace expansion generates a sequence of numbers. Use `seq` for dynamic ranges or floating-point steps.
Name
Loop over files safely
Text
Globs expand inside the shell and handle filenames with spaces correctly. Enable nullglob so an empty match produces zero iterations instead of the literal pattern.
Name
Loop over an array
Text
Always quote `"${arr[@]}"` so each element stays a single word, even if it contains spaces.
Name
Loop over command output safely
Text
Avoid `for x in $(cmd)` — it word-splits. Use a while-read loop with NUL-delimited output for arbitrary data, or process substitution with a mapfile for arrays.
Name
Control flow with break and continue
Text
Use `break` to exit the loop early and `continue` to skip to the next iteration.
Faq
Q
Why should I quote the loop variable?
A
An unquoted $item is word-split and glob-expanded. If an item is "my file.txt" or "*.log", the body sees multiple words or matching filenames instead of the original value.
Q
How do I loop with an index?
A
Use `${!arr[@]}` to get the indices: `for i in "${!arr[@]}"; do echo "$i: ${arr[$i]}"; done`.
Q
What is the difference between $@ and a for loop?
A
"$@" is the list of positional parameters; a for loop can iterate over any list. You can combine them: `for arg in "$@"; do ... done`.