Thuta Learning
BasicDevOps & Toolsbeginner

Loops: for, while, until

What you'll walk away with

  • Explain the core ideas behind Loops: for, while, until
  • Run the sample code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

A `for` loop repeats over a fixed list of items known (or computable) in advance — words you write out literally, a glob pattern like `*.log` that the shell expands into matching filenames before the loop even starts, or the classic C-style `for ((i=0; i<n; i++))` when what you actually need is a numeric counter rather than a list of words. A `while` loop instead keeps re-evaluating its condition and running as long as that condition stays true, which makes it the natural choice whenever the number of iterations isn't known ahead of time — reading input line by line with `while read`, or repeating some action until a piece of state changes. `until` is `while`'s mirror image, running as long as its condition is false rather than true; it exists purely for readability, for the cases where a loop reads more naturally as 'keep going until X happens' than as 'keep going while X hasn't happened.' Inside any of these three, `break` exits the loop immediately regardless of the condition, and `continue` skips the rest of the current iteration's body and jumps straight to the next one. One subtlety worth knowing early, because it trips up almost everyone the first time: piping a command into a `while read` loop (`cmd | while read ...`) runs the entire loop in a subshell, since a pipeline forks a separate process for each stage — so any variable the loop modifies, like a running counter, reverts to its pre-loop value the moment the loop finishes, a gotcha the lesson's own example demonstrates directly.

Connect it to a real scenario

Processing a log file line by line to count how many lines match a pattern is a textbook `while read` use case: `while IFS= read -r line; do ...; done < file.txt` reads each line safely — including ones with leading or trailing spaces, which `IFS=` preserves — without the word-splitting surprises a naive approach would hit, and it's exactly what the lesson's example does when it reads `lines.txt` and increments a `total` counter to 3. Meanwhile, a script that needs to retry a flaky network call up to five times is a natural fit for a C-style `for` loop combined with `break` — stop looping as soon as the call succeeds instead of always running all five attempts. The example's final block makes the subshell gotcha concrete: piping `printf` output into `while read` increments `count` to 3 inside the loop, but because that loop ran in a subshell forked for the pipeline, the outer `count` is still 0 once the pipeline finishes — exactly the kind of surprise that redirecting from a file (`< file.txt`) instead of piping avoids.

Try the working example

bash
#!/usr/bin/env bash
echo "for over a list:"
for color in red green blue; do
  echo "  color -> $color"
done

echo "for over a glob:"
mkdir -p /tmp/bash-lesson-loops
touch /tmp/bash-lesson-loops/a.log /tmp/bash-lesson-loops/b.log
for f in /tmp/bash-lesson-loops/*.log; do
  echo "  found -> $(basename "$f")"
done

echo "C-style for:"
for ((i = 1; i <= 3; i++)); do
  echo "  i -> $i"
done

echo "while loop:"
n=3
while [ "$n" -gt 0 ]; do
  echo "  countdown -> $n"
  n=$((n - 1))
done

echo "until loop:"
m=1
until [ "$m" -gt 3 ]; do
  echo "  climbing -> $m"
  m=$((m + 1))
done

echo "break and continue:"
for num in 1 2 3 4 5; do
  if [ "$num" -eq 2 ]; then
    continue
  fi
  if [ "$num" -eq 4 ]; then
    break
  fi
  echo "  num -> $num"
done

echo "while read from a file:"
printf 'line one\nline two\nline three\n' > /tmp/bash-lesson-loops/lines.txt
total=0
while IFS= read -r line; do
  echo "  read -> $line"
  total=$((total + 1))
done < /tmp/bash-lesson-loops/lines.txt
echo "total lines counted: $total"

echo "the subshell gotcha (piped while read):"
count=0
printf 'x\ny\nz\n' | while IFS= read -r _; do
  count=$((count + 1))
done
echo "count after piped loop: $count"

rm -rf /tmp/bash-lesson-loops
You should see
Running this script prints:

for over a list:
  color -> red
  color -> green
  color -> blue
for over a glob:
  found -> a.log
  found -> b.log
C-style for:
  i -> 1
  i -> 2
  i -> 3
while loop:
  countdown -> 3
  countdown -> 2
  countdown -> 1
until loop:
  climbing -> 1
  climbing -> 2
  climbing -> 3
break and continue:
  num -> 1
  num -> 3
while read from a file:
  read -> line one
  read -> line two
  read -> line three
total lines counted: 3
the subshell gotcha (piped while read):
count after piped loop: 0

5-minute try-it

Write a script that creates three files named task1.txt, task2.txt, task3.txt, loops over them with a for-loop glob to print each filename, then separately uses a while loop with a counter to print a countdown from 5 to 1.

One important caution

Piping a command into while read (cmd | while read ...) and then being confused why a counter incremented inside the loop is back to its original value afterward — the pipe runs the loop in a subshell

Forgetting -r on read, so backslashes in the input get interpreted/stripped instead of being read literally

GNU Bash Reference Manual — Looping ConstructsBash / Shell Scripting

Easy traps

  • Piping a command into while read (cmd | while read ...) and then being confused why a counter incremented inside the loop is back to its original value afterward — the pipe runs the loop in a subshell
  • Forgetting -r on read, so backslashes in the input get interpreted/stripped instead of being read literally
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Write a script that creates three files named task1.txt, task2.txt, task3.txt, loops over them with a for-loop glob to print each filename, then separately uses a while loop with a counter to print a countdown from 5 to 1.

You'll know it worked when: Running this script prints: for over a list: color -> red color -> green color -> blue for over a glob: found -> a.log found -> b.log C-style for: i -> 1 i -> 2 i -> 3 while loop: countdown -> 3 countdown -> 2 countdown -> 1 until loop: climbing -> 1 climbing -> 2 climbing -> 3 break and continue: num -> 1 num -> 3 while read from a file: read -> line one read -> line two read -> line three total lines counted: 3 the subshell gotcha (piped while read): count after piped loop: 0

Loops: for, while, until | Thuta Learning