Course outline · 0% complete

0/29 lessons0%

Course overview →

while loops and reading lines

lesson 9-2 · ~9 min · 25/29

Repeat while a condition holds

for walks a known list. while repeats as long as a condition command succeeds, using the same [ ] tests from lesson 8-3:

count=1
while [ "$count" -le 3 ]; do
  echo "lap $count"
  count=$((count + 1))
done

New piece: $(( )) is arithmetic expansion, the shell's calculator. $((count + 1)) evaluates to the sum. Forget the increment line and the condition never becomes false: an infinite loop. (Ctrl+C from lesson 6-2 is the escape hatch.)

A counter climbing to its limit

The variable starts at 1, the condition checks it against 3, and the body both prints and increments.

count=1
while [ "$count" -le 3 ]; do
  echo "lap $count"
  count=$((count + 1))
done
echo "race over"

Output

lap 1
lap 2
lap 3
race over

The loop ends because the body changes the thing the condition tests. Once count reaches 4, the test [ "$count" -le 3 ] exits non-zero, while stops repeating, and control falls through to the final echo.

The classic: process a file line by line

read -r (from lesson 8-3) grabs one line of stdin. Put it as a while condition and redirect a file into the loop with < (lesson 5-1), and you get the standard line-by-line pattern:

while read -r name; do
  echo "Welcome, $name"
done < guests.txt

read succeeds (exit 0) for every line and fails at end of file, which ends the loop. Every log processor, CSV importer, and batch job in shell history is built on this shape.

Greeting every line of a file

Three names go into a file, and the while-read loop handles them one line at a time.

echo "Ada" > guests.txt
echo "Grace" >> guests.txt
echo "Linus" >> guests.txt
while read -r name; do
  echo "Welcome, $name"
done < guests.txt

Output

Welcome, Ada
Welcome, Grace
Welcome, Linus

The < guests.txt at the very end is easy to overlook and essential: it attaches the file to the whole loop, not to any single command inside it, so each read pulls the next line from the same stream. Nothing in the loop mentions how many names there are, so the same code handles three guests or three thousand.

A while read -r line; do ...; done < file loop ends when read runs out of input. read returns exit code 0 for as long as lines keep arriving, and a non-zero code once it hits end of file.

Since while decides whether to continue purely from exit codes, which is lesson 8-3's idea again, that failing read is what stops the loop. The loop therefore terminates exactly when the input is exhausted, with no need to count the lines in advance.

Accumulating a running total combines a loop with arithmetic expansion. The variable total starts at 0 outside the loop, each pass adds one number to it, and the result is printed once the loop has finished.

total=0
for n in 1 2 3 4 5; do
  total=$((total + n))
done
echo "sum: $total"

Output

sum: 15

Two placement details make this correct. The accumulator is initialized before the loop, so it is not reset on every pass, and the echo comes after done, so the final sum prints once instead of five partial sums printing along the way.