LFCS 124 ๐ง Shell Scripting โ Loops
The previous chapter covered conditionals: the if statement, the test command, and the case statement. This chapter covers loops: for, while, and until. These are the constructs that repeat a block of commands. The LFCS exam tests the syntax of the for loop over a list and over a range, the while loop with a condition, the until loop with an inverted condition, and the loop control commands break and continue. The critical trap is the loop that never terminates: a while loop with a condition that never becomes false, or a for loop that re-reads a file without changing the pointer, or a loop that consumes arguments with shift but forgets to terminate when the arguments run out.
Key point: The for loop iterates over a list of words. The list can be literal (for i in 1 2 3), a range (for i in {1..10}), a command substitution (for f in $(ls)), or the positional parameters (for arg in "$@"). The while loop runs as long as the condition returns zero (true). The until loop runs as long as the condition returns non-zero (false). The break command exits the loop entirely. The continue command skips the rest of the current iteration and proceeds to the next.
Why loops require a termination guarantee
The infinite loop problem. A while loop runs until its condition is false. If the condition is always true, the loop never terminates. The classic mistake is while [ "$input" != "quit" ] without a read inside the loop to update the variable. The fix is to ensure that every iteration modifies a variable that the condition depends on, or to add a counter that limits the iterations.
The word-splitting problem. A for loop iterates over words, not lines. The for line in $(cat file) form splits the file’s contents on whitespace, not on newlines. A line with spaces becomes multiple iterations. The fix is while IFS= read -r line for line-by-line iteration, or for file in *.txt for filename iteration.
The subshell problem. A while loop that reads from a pipe (cat file | while read line) runs in a subshell. Variables set inside the loop do not persist after the loop. The fix is to use process substitution (while read line; do ...; done < file) or a here-string (while read line; do ...; done <<< "$data").
The break versus continue problem. The break command exits the loop. The continue command skips the rest of the current iteration but stays in the loop. The distinction matters in nested loops: break 2 exits two levels, continue 2 skips to the next iteration of the outer loop.
The trade-off. The for loop is for a known set of items. The while loop is for an unknown number of iterations, determined by a condition. The until loop is the same as while with the condition negated, which is useful when the “true” case is the one to exit on. The for loop is the most common; the while loop is for reading input; the until loop is the least common but is the correct choice for “keep trying until success”.
a. The for loop: lists, ranges, and files
The for loop assigns each item in the list to the loop variable and runs the body. The list is expanded before the loop starts.
#!/bin/bash
for name in alice bob charlie; do
echo "Hello, $name"
done
The list can be a brace expansion for a numeric or character range.
# Numeric range
for i in {1..5}; do
echo "Number: $i"
done
# Numeric range with step
for i in {0..10..2}; do
echo "Even: $i"
done
# Character range
for letter in {a..e}; do
echo "Letter: $letter"
done
The brace expansion is a bash feature. In POSIX shell, the equivalent is a while loop with an arithmetic counter, or the seq command: for i in $(seq 1 5).
The for loop over filenames is the correct way to process files. The glob expansion is performed by the shell before the loop starts.
for file in *.txt; do
echo "Processing $file"
# Process the file
done
If no files match the glob, the loop runs once with the literal string *.txt. The nullglob shell option changes this behavior: with shopt -s nullglob, the glob expands to nothing, and the loop does not run.
The C-style for loop is a bash extension. It is useful for iteration with an arbitrary step or complex initialization.
for ((i = 0; i < 10; i++)); do
echo "Iteration: $i"
done
b. The while and until loops: conditions
The while loop runs as long as the condition returns zero. The until loop runs as long as the condition returns non-zero. The two are equivalent, with the condition negated.
#!/bin/bash
count=1
while [ "$count" -le 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
The count=$((count + 1)) increments the counter. Without it, the condition [ "$count" -le 5 ] is always true, and the loop is infinite.
The while loop with read is the standard pattern for processing a file line by line.
#!/bin/bash
while IFS= read -r line; do
echo "Line: $line"
done < file.txt
The IFS= clears the internal field separator, which prevents leading and trailing whitespace from being trimmed. The -r flag prevents backslash escapes from being interpreted. The < file.txt redirects the file into the loop, which runs in the current shell, not a subshell.
The until loop runs until the condition is true. It is the correct choice for “retry until success”.
#!/bin/bash
attempt=0
until curl -sf https://example.com/health; do
attempt=$((attempt + 1))
if [ "$attempt" -ge 5 ]; then
echo "Failed after 5 attempts" >&2
exit 1
fi
sleep 2
done
echo "Service is healthy"
The loop runs the curl command. If it fails, the loop increments the attempt counter and waits. If it succeeds, the loop exits. The attempt counter prevents an infinite loop.
c. The break and continue commands
The break command exits the loop. The continue command skips to the next iteration.
#!/bin/bash
for i in {1..10}; do
if [ "$i" -eq 5 ]; then
break
fi
echo "Number: $i"
done
# Output: 1, 2, 3, 4
The break exits the loop when i is 5. The numbers 6 through 10 are not printed.
#!/bin/bash
for i in {1..5}; do
if [ "$i" -eq 3 ]; then
continue
fi
echo "Number: $i"
done
# Output: 1, 2, 4, 5
The continue skips the echo when i is 3. The loop continues with 4.
The break N and continue N forms work in nested loops. The N is the number of levels to break or continue.
for i in {1..3}; do
for j in {1..3}; do
if [ "$j" -eq 2 ]; then
break 2
fi
echo "$i,$j"
done
done
# Output: 1,1
The break 2 exits both the inner and outer loops. The output is only 1,1.
Complete Example Session
# ============================================
# PART 1: FOR LOOP OVER LIST
# ============================================
cat > list.sh <<'EOF'
#!/bin/bash
for name in alice bob charlie; do
echo "Hello, $name"
done
EOF
chmod +x list.sh
./list.sh
# Output:
# Hello, alice
# Hello, bob
# Hello, charlie
# ============================================
# PART 2: FOR LOOP OVER RANGE
# ============================================
cat > range.sh <<'EOF'
#!/bin/bash
for i in {1..5}; do
echo "Number: $i"
done
EOF
chmod +x range.sh
./range.sh
# Output: 1, 2, 3, 4, 5
# ============================================
# PART 3: FOR LOOP OVER FILES
# ============================================
for file in *.txt; do
echo "File: $file"
done
# Lists all .txt files.
# ============================================
# PART 4: WHILE LOOP
# ============================================
cat > count.sh <<'EOF'
#!/bin/bash
count=1
while [ "$count" -le 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
EOF
chmod +x count.sh
./count.sh
# Output: 1, 2, 3, 4, 5
# ============================================
# PART 5: WHILE READ
# ============================================
cat > read.sh <<'EOF'
#!/bin/bash
while IFS= read -r line; do
echo "Line: $line"
done < "$1"
EOF
chmod +x read.sh
./read.sh /etc/hostname
# Output: Line: <hostname>
# ============================================
# PART 6: UNTIL LOOP
# ============================================
cat > until.sh <<'EOF'
#!/bin/bash
count=1
until [ "$count" -gt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
EOF
chmod +x until.sh
./until.sh
# Output: 1, 2, 3, 4, 5
# ============================================
# PART 7: BREAK
# ============================================
cat > brk.sh <<'EOF'
#!/bin/bash
for i in {1..10}; do
if [ "$i" -eq 5 ]; then
break
fi
echo "$i"
done
EOF
chmod +x brk.sh
./brk.sh
# Output: 1, 2, 3, 4
# ============================================
# PART 8: CONTINUE
# ============================================
cat > cont.sh <<'EOF'
#!/bin/bash
for i in {1..5}; do
if [ "$i" -eq 3 ]; then
continue
fi
echo "$i"
done
EOF
chmod +x cont.sh
./cont.sh
# Output: 1, 2, 4, 5
# ============================================
# PART 9: NESTED BREAK
# ============================================
cat > nested.sh <<'EOF'
#!/bin/bash
for i in {1..3}; do
for j in {1..3}; do
if [ "$j" -eq 2 ]; then
break 2
fi
echo "$i,$j"
done
done
EOF
chmod +x nested.sh
./nested.sh
# Output: 1,1
The nine parts trace the loops: for over list, for over range, for over files, while, while read, until, break, continue, and break 2.
Quick Reference
Loop Types
| Loop | Condition | Use Case |
|---|---|---|
for | List of items | Known set |
while | Command succeeds | Unknown count |
until | Command fails | Retry until success |
for (( )) | C-style expression | Counter with step |
Loop Control
| Command | Effect |
|---|---|
break | Exit loop |
break N | Exit N levels |
continue | Skip iteration |
continue N | Skip to Nth loop |
Read Patterns
| Pattern | Purpose |
|---|---|
while read line | Line iteration |
while IFS= read -r line | Preserve whitespace and backslashes |
while read -r line < file | Read from file |
Common Ranges
| Range | Meaning |
|---|---|
{1..5} | 1 through 5 |
{0..10..2} | 0, 2, 4, 6, 8, 10 |
{a..e} | a through e |
seq 1 5 | POSIX alternative |
Best Practices
โ Do This:
while IFS= read -r line; do
echo "$line"
done < file.txt
<!-- Preserve whitespace and backslashes --> # โ
for arg in "$@"; do
echo "$arg"
done
<!-- Quote positional parameters --> # โ
for i in {1..10}; do
if [ "$i" -eq 5 ]; then break; fi
done
<!-- Explicit break --> # โ
โ Don’t Do This:
for line in $(cat file.txt); do
<!-- Splits on whitespace, not newlines --> # โ
while [ "$x" != "quit" ]; do
echo "$x"
done
<!-- x never changes, infinite loop --> # โ
cat file.txt | while read line; do
<!-- Subshell: variables lost after loop --> # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Infinite loop | Condition never false | Update the variable |
| Filename with spaces fails | Unquoted $file | "$file" |
| Lines with spaces split | $(cat file) | while read |
| Variables lost after loop | Pipe subshell | < file redirect |
| Glob runs once if no files | No nullglob | shopt -s nullglob |
Real-World Examples
1. For Over List
for name in alice bob; do echo "$name"; done
2. For Over Range
for i in {1..5}; do echo "$i"; done
3. For Over Files
for f in *.txt; do echo "$f"; done
4. C-Style For
for ((i=0; i<10; i++)); do echo "$i"; done
5. While Counter
while [ "$count" -le 5 ]; do count=$((count+1)); done
6. While Read
while IFS= read -r line; do echo "$line"; done < file
7. Until Retry
until curl -sf https://api/health; do sleep 2; done
8. Break
for i in {1..10}; do if [ "$i" -eq 5 ]; then break; fi; done
9. Continue
for i in {1..10}; do if [ "$i" -eq 3 ]; then continue; fi; echo "$i"; done
10. Nested Break
for i in {1..3}; do for j in {1..3}; do break 2; done; done
Visual
for Loop Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ for item in list โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ More items? โ
โ โโ NO โ exit loop โ
โ โโ YES โ run body with current item โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Next iteration โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
while vs until
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ while [ condition ] โ
โ โ Run while condition is true (exit 0) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ until [ condition ] โ
โ โ Run while condition is false (exit != 0) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| List iteration | for item in list |
| Range iteration | for i in {1..5} |
| C-style | for ((i=0; i<10; i++)) |
| Condition loop | while [ condition ] |
| Inverse condition | until [ condition ] |
| Line read | while IFS= read -r line |
| Exit loop | break |
| Skip iteration | continue |
| Nested control | break 2, continue 2 |
Key takeaways:
- The
forloop is for a known set of items. The list can be literal, a brace expansion, a glob, or positional parameters. The loop variable takes each item in turn. - The
whileanduntilloops are for conditions. Thewhileloop runs while the condition is true. Theuntilloop runs while the condition is false. Both require the condition to change inside the loop. - The
while readpattern is the correct way to process files line by line. TheIFS=and-rflags preserve whitespace and backslashes. The< fileredirect keeps the loop in the current shell. - The
breakandcontinuecommands control the flow. Thebreakexits the loop. Thecontinueskips to the next iteration. Thebreak Nandcontinue Nforms work in nested loops. - The infinite loop is the primary failure mode. The condition must depend on a variable that the loop body modifies. A counter is the simplest guarantee.
Remember: The loop is the iteration construct. The for loop is for the known set. The while loop is for the condition. The until loop is for the retry. The break is the exit. The continue is the skip. The while read is the file reader. The exam tests the syntax and the termination condition.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!