Loops
Loops#
Concepts#
for Loop#
Iterating Over a List#
for item in value1 value2 value3; do
echo "$item"
done
# Loop over words
for color in red green blue; do
echo "Color: $color"
done
# Loop over files
for file in /etc/*.conf; do
echo "Config file: $file"
done
# Loop over command output
for user in $(cut -d: -f1 /etc/passwd | head -5); do
echo "User: $user"
done
# Loop over array elements
fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# Loop over arguments
for arg in "$@"; do
echo "Argument: $arg"
done
C-Style for Loop#
for ((i = 0; i < 10; i++)); do
echo "i = $i"
done
# Count by 2
for ((i = 0; i <= 20; i += 2)); do
echo "$i"
done
Brace Expansion#
# Range of numbers
for i in {1..5}; do
echo "Number: $i"
done
# Range with step
for i in {0..100..10}; do
echo "$i"
done
# Letters
for letter in {a..z}; do
echo "$letter"
done
Note: Brace expansion
{1..5}is a Bash feature. It does not work insh/dash.
while Loop#
Repeats as long as the condition is true:
while condition; do
commands
done
# Count down
count=5
while [[ $count -gt 0 ]]; do
echo "Count: $count"
((count--))
done
echo "Done!"
# Wait for a file to appear
while [[ ! -f /tmp/ready.flag ]]; do
echo "Waiting..."
sleep 2
done
echo "File appeared!"
# Infinite loop (common for service scripts)
while true; do
echo "Running... (Ctrl+C to stop)"
sleep 1
done
until Loop#
Repeats as long as the condition is false (opposite of while):
until condition; do
commands
done
count=0
until [[ $count -ge 5 ]]; do
echo "Count: $count"
((count++))
done
until is less common than while — they are interchangeable by negating the condition.
Reading Files Line by Line#
The most reliable way to process a file line by line:
while IFS= read -r line; do
echo "Line: $line"
done < filename.txt
IFS=— prevents leading/trailing whitespace from being stripped-r— prevents backslashes from being interpreted as escape characters< filename.txt— redirects the file into the loop’s stdin
# Process /etc/passwd
while IFS=: read -r username _ uid _ _ homedir shell; do
if [[ $uid -ge 1000 && "$shell" != "/usr/sbin/nologin" ]]; then
echo "User: $username (UID: $uid, Home: $homedir, Shell: $shell)"
fi
done < /etc/passwd
Why not for line in $(cat file)? Because:
- Word splitting breaks lines with spaces
- Globbing expands
*in the content - Entire file is read into memory at once
break and continue#
# break — exit the loop entirely
for i in {1..10}; do
if [[ $i -eq 5 ]]; then
break
fi
echo "$i"
done
# Prints: 1 2 3 4
# continue — skip the rest of this iteration
for i in {1..10}; do
if [[ $((i % 2)) -eq 0 ]]; then
continue
fi
echo "$i"
done
# Prints: 1 3 5 7 9 (odd numbers only)
Loop Output Redirection#
You can redirect the output of an entire loop:
for file in /etc/*.conf; do
echo "$file"
done > conffiles.txt
# All output goes to the file, not the screen
while read -r line; do
echo "Processed: $line"
done < input.txt | sort > output.txt
# Pipe the loop's output to sort, then to a file
Nested Loops#
for i in {1..3}; do
for j in {1..3}; do
echo "($i, $j)"
done
done
Processing Command Output#
# Pipe into while loop
ps aux | while read -r user pid cpu mem vsz rss tty stat start time cmd; do
if (( $(echo "$mem > 5.0" | bc -l 2>/dev/null) )); then
echo "High memory: $user $pid $mem% $cmd"
fi
done
# Using process substitution
while read -r line; do
echo "Log: $line"
done < <(journalctl -n 5 --no-pager)
Lab#
Exercise 1: for Loop Basics#
cd ~/lab/scripts
cat > forloop.sh << 'SCRIPT'
#!/bin/bash
# Loop over a list
echo "=== Colors ==="
for color in red green blue yellow; do
echo " $color"
done
# Loop with brace expansion
echo ""
echo "=== Numbers 1-5 ==="
for i in {1..5}; do
echo " Number: $i"
done
# C-style loop
echo ""
echo "=== Countdown ==="
for ((i = 5; i >= 1; i--)); do
echo " $i..."
done
echo " Go!"
SCRIPT
chmod +x forloop.sh
./forloop.sh
Exercise 2: Loop Over Files#
cd ~/lab/scripts
cat > fileloop.sh << 'SCRIPT'
#!/bin/bash
echo "=== Config files in /etc/ (first 10) ==="
count=0
for file in /etc/*.conf; do
if [[ -f "$file" ]]; then
size=$(wc -c < "$file")
echo " $(basename "$file") — $size bytes"
((count++))
[[ $count -ge 10 ]] && break
fi
done
echo "Showed $count files."
SCRIPT
chmod +x fileloop.sh
./fileloop.sh
Exercise 3: while Loop and Input#
cd ~/lab/scripts
cat > countdown.sh << 'SCRIPT'
#!/bin/bash
# Countdown timer
read -p "Countdown from? " start
if [[ ! "$start" =~ ^[0-9]+$ ]]; then
echo "Please enter a number."
exit 1
fi
while [[ $start -gt 0 ]]; do
echo "$start..."
((start--))
sleep 1
done
echo "Time's up!"
SCRIPT
chmod +x countdown.sh
./countdown.sh
Exercise 4: Read a File Line by Line#
cd ~/lab/scripts
# Create a sample data file
cat > users.csv << 'EOF'
Alice,Engineering,85000
Bob,Marketing,72000
Carol,Engineering,92000
Dave,Sales,68000
Eve,Marketing,78000
EOF
cat > readcsv.sh << 'SCRIPT'
#!/bin/bash
# Read and process a CSV file
echo "=== Employee Report ==="
printf "%-10s %-15s %10s\n" "Name" "Department" "Salary"
echo "--------------------------------------"
while IFS=, read -r name dept salary; do
printf "%-10s %-15s %10s\n" "$name" "$dept" "$salary"
done < users.csv
echo ""
# Calculate total salary
total=0
while IFS=, read -r _ _ salary; do
total=$((total + salary))
done < users.csv
echo "Total payroll: \$$total"
SCRIPT
chmod +x readcsv.sh
./readcsv.sh
Exercise 5: break and continue#
cd ~/lab/scripts
cat > breakcontinue.sh << 'SCRIPT'
#!/bin/bash
echo "=== Find first .sh file in current directory ==="
for file in *; do
if [[ "$file" == *.sh ]]; then
echo "Found: $file"
break
fi
done
echo ""
echo "=== Skip .csv files, list everything else ==="
for file in *; do
if [[ "$file" == *.csv ]]; then
continue
fi
echo " $file"
done
SCRIPT
chmod +x breakcontinue.sh
./breakcontinue.sh
Exercise 6: Multiplication Table#
cd ~/lab/scripts
cat > multitable.sh << 'SCRIPT'
#!/bin/bash
# Generate a multiplication table
n=${1:-5}
for ((i = 1; i <= n; i++)); do
for ((j = 1; j <= n; j++)); do
printf "%4d" $((i * j))
done
echo ""
done
SCRIPT
chmod +x multitable.sh
./multitable.sh
./multitable.sh 10
Review#
1. What is the difference between `while` and `until`?
while loops as long as the condition is true. until loops as long as the condition is false. They are functionally interchangeable by negating the condition.
2. What is the correct way to read a file line by line?
while IFS= read -r line; do ... done < file. IFS= preserves whitespace, -r prevents backslash interpretation. Do NOT use for line in $(cat file).
3. What does `break` do in a loop?
It immediately exits the loop and continues with the code after the loop. break 2 exits two nested loops.
4. What does `continue` do in a loop?
It skips the rest of the current iteration and goes to the next iteration of the loop.
5. How do you loop over numbers 1 through 100 in Bash?
for i in {1..100}; do ... done (brace expansion) or for ((i = 1; i <= 100; i++)); do ... done (C-style).
6. Why should you avoid `for line in $(cat file)`?
Word splitting breaks lines with spaces into multiple iterations. Globbing expands * characters in the content. The entire file is read into memory at once. Use while read instead.
Previous: Conditionals | Next: Functions and Error Handling