Table of Contents
- For Loops
- While Loops
- Until Loops
- Nested Loops
- Loop Control Statements
- Practical Examples & Use Cases
- Common Pitfalls & Best Practices
- Conclusion
- References
For Loops
For loops are ideal for iterating over a predefined list of items (e.g., filenames, numbers, or strings). Bash offers several variations to suit different needs.
Basic For Loop Syntax
The simplest for loop iterates over a list of items:
for item in list; do
# Commands to execute for each item
done
item: A variable that takes the value of each element inlistduring iteration.list: A space-separated sequence of values (e.g.,1 2 3,file1.txt file2.txt).
Iterating Over a List
Loop through a list of strings or filenames:
# Iterate over a list of fruits
for fruit in apple banana "cherry pie" date; do
echo "I like $fruit"
done
Output:
I like apple
I like banana
I like cherry pie
I like date
Note: Enclose items with spaces (e.g., “cherry pie”) in quotes to treat them as a single item.
Using Ranges with {start..end}
For numerical or alphabetical ranges, use {start..end} to generate a sequence:
# Iterate from 1 to 5
for num in {1..5}; do
echo "Number: $num"
done
# Iterate over letters A to D
for letter in {A..D}; do
echo "Letter: $letter"
done
Output:
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Letter: A
Letter: B
Letter: C
Letter: D
Command Substitution in For Loops
Use $(command) to dynamically generate the list of items (e.g., loop through files in a directory):
# List all .txt files in the current directory
for file in $(ls *.txt); do
echo "Processing: $file"
done
Caution: Avoid ls in loops for filenames with spaces. Use for file in *.txt instead (Bash expands *.txt safely).
C-Style For Loops
For more control (e.g., custom increments/decrements), use C-style syntax with arithmetic expressions:
# Syntax: for ((initialization; condition; increment)); do ... done
for ((i=0; i<5; i++)); do
echo "Count: $i"
done
# Decrement example (countdown from 3 to 0)
for ((i=3; i>=0; i--)); do
echo "T-minus $i..."
done
Output:
Count: 0
Count: 1
Count: 2
Count: 3
Count: 4
T-minus 3...
T-minus 2...
T-minus 1...
T-minus 0...
While Loops
While loops run a block of code as long as a condition is true. They’re useful for tasks where the number of iterations isn’t known in advance (e.g., waiting for user input or a system event).
Basic While Loop Syntax
while [ condition ]; do
# Commands to execute while condition is true
done
condition: A test expression (e.g.,$count -lt 5,-f "file.txt"). Use[ ](POSIX test) or[[ ]](Bash-specific, supports patterns).
Reading Input with while read
A common use case is reading lines from a file or user input:
# Read lines from a file (e.g., "names.txt")
while IFS= read -r line; do
echo "Hello, $line!"
done < "names.txt"
IFS=: Prevents trimming of leading/trailing whitespace.-r: Disables backslash escapes (treats\as a literal character).< "names.txt": Redirects the file into the loop.
Infinite While Loops
Use while true for infinite loops (terminate with Ctrl+C or break):
# Infinite loop with a 2-second delay
while true; do
echo "This runs forever (press Ctrl+C to stop)..."
sleep 2
done
Arithmetic Conditions in While Loops
Use (( )) for arithmetic conditions (e.g., incrementing a counter):
count=0
while (( count < 3 )); do # Equivalent to [ $count -lt 3 ]
echo "Count: $count"
((count++)) # Increment count by 1
done
Output:
Count: 0
Count: 1
Count: 2
Until Loops
Until loops are the inverse of while loops: they run until a condition becomes true. Use them when you want to repeat a task until a target state is reached.
Basic Until Loop Syntax
until [ condition ]; do
# Commands to execute until condition is true
done
Practical Use Case: Waiting for a Condition
Example: Wait until a file (data.txt) exists before proceeding:
until [ -f "data.txt" ]; do
echo "Waiting for data.txt..."
sleep 1 # Check every second
done
echo "data.txt found!"
Output (if data.txt is created after 3 seconds):
Waiting for data.txt...
Waiting for data.txt...
Waiting for data.txt...
data.txt found!
Nested Loops
Nested loops are loops inside other loops. They’re useful for multi-dimensional tasks (e.g., rows and columns, or files in subdirectories).
Example: Multi-Dimensional Iteration
Print a 3x3 grid of numbers:
# Outer loop (rows)
for ((row=1; row<=3; row++)); do
# Inner loop (columns)
for ((col=1; col<=3; col++)); do
echo -n "$row,$col " # -n: No newline
done
echo # Newline after each row
done
Output:
1,1 1,2 1,3
2,1 2,2 2,3
3,1 3,2 3,3
Loop Control Statements
Bash provides break and continue to modify loop behavior.
Break: Exit a Loop Early
break terminates the nearest enclosing loop:
# Break when count reaches 3
for ((i=1; i<=5; i++)); do
if ((i == 3)); then
break # Exit loop
fi
echo "i = $i"
done
Output:
i = 1
i = 2
Continue: Skip to the Next Iteration
continue skips the rest of the current iteration and moves to the next:
# Skip even numbers
for ((i=1; i<=5; i++)); do
if ((i % 2 == 0)); then
continue
fi
echo "Odd number: $i"
done
Output:
Odd number: 1
Odd number: 3
Odd number: 5
Practical Examples & Use Cases
Example 1: Backup Log Files
Use a for loop to back up .log files with timestamps:
LOG_DIR="/var/log"
BACKUP_DIR="$HOME/log_backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Loop through log files
for logfile in "$LOG_DIR"/*.log; do
# Skip if not a regular file
[ -f "$logfile" ] || continue
# Backup the file with timestamp
cp "$logfile" "$BACKUP_DIR/$(basename "$logfile").$TIMESTAMP.bak"
echo "Backed up: $(basename "$logfile")"
done
Example 2: Monitor Disk Space
Use a while loop to check disk space every 5 minutes:
THRESHOLD=90 # Alert if usage > 90%
MOUNT_POINT="/"
while true; do
# Get disk usage percentage (e.g., "85" for 85%)
USAGE=$(df -P "$MOUNT_POINT" | awk 'NR==2 {print $5}' | sed 's/%//')
if (( USAGE > THRESHOLD )); then
echo "ALERT: Disk usage on $MOUNT_POINT is $USAGE% (over $THRESHOLD%)"
# Add notification logic here (e.g., email, Slack)
else
echo "Disk usage on $MOUNT_POINT: $USAGE%"
fi
sleep 300 # Check every 5 minutes (300 seconds)
done
Common Pitfalls & Best Practices
-
Unquoted Variables: Always quote variables (e.g.,
"$file") to handle filenames with spaces:# Bad: Splits "cherry pie.txt" into "cherry" and "pie.txt" for file in $files; do ... done # Good: Treats "cherry pie.txt" as one item for file in "$files"; do ... done -
Infinite Loops: Ensure loop conditions eventually change. For example, in
while ((i < 5)), incrementiwith((i++)). -
Overusing
lsin For Loops: Use glob patterns (*.txt) instead of$(ls *.txt)to avoid issues with special characters. -
Arithmetic in
[ ]: Use(( ))for arithmetic (e.g.,((i++))) instead of[ $i -eq 5 ]for better readability. -
Test with
echoFirst: Before running destructive commands (e.g.,rm,cp), test loops withechoto verify behavior:# Test first for file in *.txt; do echo "Would delete: $file"; done # Then run for file in *.txt; do rm "$file"; done
Conclusion
Loops are the workhorses of Bash scripting, enabling automation of repetitive tasks with precision and flexibility. By mastering for, while, and until loops—along with control statements like break and continue—you’ll be able to write scripts that handle everything from file processing to system monitoring.
Practice with real-world scenarios (e.g., backups, log analysis) to solidify your understanding. Remember to follow best practices like quoting variables and testing conditions to avoid common pitfalls.