Step 1 of 5 · Reading · ~3 min
Learn
Control Flow
Loops
By the end of this lesson you can repeat commands over a list of words, a range of numbers, or a condition, read input line by line, and avoid the one famous trap that appears when the input is lines rather than words. Bash loops iterate over the things shells care about: words, lines, files and number ranges. The syntax is small. The skill is choosing what to feed the loop.
for: over a list of words
for color in red green blue; do
echo "paint it $color"
done
for x in LIST walks the words after in. Those words usually come from an expansion:
for f in *.log; do # a glob: every .log file, names with spaces intact
echo "checking $f"
done
for i in {1..4}; do # brace expansion: 1 2 3 4, inclusive at both ends
echo "round $i"
done
Brace expansion is done before variables are substituted, so {1..$n} is the literal text {1..$n}, which is a silent bug. When the bound is a variable, use seq or the C-style loop:
for i in $(seq 1 "$n"); do # seq prints the numbers, the loop splits them
echo "$i"
done
for (( i = 1; i <= n; i++ )); do # C-style: arithmetic rules apply inside
echo "$i"
done
while: a condition, or the lines of input
while runs a command and repeats the body as long as it succeeds. With an arithmetic condition it is the loop for "until something changes":
size=1000
halvings=0
while (( size >= 10 )); do
size=$(( size / 2 ))
halvings=$(( halvings + 1 ))
done
echo "$halvings" # 7: 1000, 500, 250, 125, 62, 31, 15, then 7 is below 10
Whatever the condition looks at must change inside the body, or the loop never ends. Check the first and last passes by hand: does the body run when the condition is already false, and does it stop on the right value?
The other everyday job for while is reading input line by line:
while read -r line; do
echo "got: $line"
done
read succeeds while there is a line to read and fails at the end of input, so the loop consumes all of stdin. Write -r always. The tempting alternative, for line in $(cat file), is wrong: the unquoted expansion splits on all whitespace, so a line with spaces becomes several iterations. Lines want while read -r; words want for.
break leaves the loop and continue jumps to the next iteration. An endless loop is while true; do ... done, where true is just a command that always succeeds.
Common mistakes
- Off by one.
i < nstops one short ofi <= n. Trace the loop for the smallest input. - Updating the wrong variable. A
whilewhose body never changes what its condition tests runs forever. {1..$n}. Braces expand before variables, so the loop body runs once with the text{1..5}.- Splitting lines on spaces.
for line in $(cat file)iterates over words, not lines. - Printing inside the loop. An
echoof the running total inside the body prints one line per iteration instead of one answer at the end.
Your exercise
Follow a number through a rule that is applied repeatedly until it reaches 1, counting the applications and tracking the largest value met on the way. The number of repetitions is not known in advance, which is what while is for. The starter prints both results; the loop that produces them is yours. Check input 1, which needs no steps at all.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…