Loops
Loops allow you to execute a block of code repeatedly, making them essential for automating repetitive tasks. Bash provides several types of loops, each suited for different scenarios.
for Loops
The for loop is the most commonly used loop in Bash scripting.
Basic for Loop Syntax
for variable in list; do
# commands
doneSimple Examples
#!/bin/bash
# Loop through a list of items
for fruit in apple banana orange; do
echo "I like $fruit"
done
# Loop through numbers
for number in 1 2 3 4 5; do
echo "Number: $number"
doneLooping Through Files
#!/bin/bash
# Process all .txt files in current directory
for file in *.txt; do
if [ -f "$file" ]; then
echo "Processing: $file"
wc -l "$file"
fi
done
# Process files in a specific directory
for file in /var/log/*.log; do
if [ -f "$file" ]; then
echo "Log file: $(basename "$file")"
echo "Size: $(du -h "$file" | cut -f1)"
fi
doneC-style for Loop
#!/bin/bash
# C-style for loop
for ((i=1; i<=10; i++)); do
echo "Iteration: $i"
done
# Counting backwards
for ((i=10; i>=1; i--)); do
echo "Countdown: $i"
done
# Step by 2
for ((i=0; i<=20; i+=2)); do
echo "Even number: $i"
doneUsing Brace Expansion
#!/bin/bash
# Loop through a range of numbers
for i in {1..10}; do
echo "Number: $i"
done
# Loop with step
for i in {1..20..2}; do
echo "Odd number: $i"
done
# Loop through letters
for letter in {a..z}; do
echo "Letter: $letter"
doneLooping Through Arrays
#!/bin/bash
# Define an array
colors=("red" "green" "blue" "yellow")
# Loop through array elements
for color in "${colors[@]}"; do
echo "Color: $color"
done
# Loop through array indices
for i in "${!colors[@]}"; do
echo "Index $i: ${colors[i]}"
donewhile Loops
The while loop continues executing as long as a condition is true.
Basic while Loop
#!/bin/bash
counter=1
while [ $counter -le 5 ]; do
echo "Counter: $counter"
((counter++))
doneReading File Line by Line
#!/bin/bash
filename="data.txt"
# Create sample file
cat > "$filename" << EOF
Line 1
Line 2
Line 3
EOF
# Read file line by line
while IFS= read -r line; do
echo "Processing: $line"
done < "$filename"Monitoring with while Loop
#!/bin/bash
# Monitor system load
echo "Monitoring system load (Ctrl+C to stop)..."
while true; do
load=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
echo "$(date): Load average: $load"
# Alert if load is high
if (( $(echo "$load > 2.0" | bc -l) )); then
echo "WARNING: High system load!"
fi
sleep 5
doneuntil Loops
The until loop continues executing until a condition becomes true (opposite of while).
Basic until Loop
#!/bin/bash
counter=1
until [ $counter -gt 5 ]; do
echo "Counter: $counter"
((counter++))
doneWaiting for a Service
#!/bin/bash
service_name="nginx"
echo "Waiting for $service_name to start..."
until systemctl is-active --quiet "$service_name"; do
echo "Service not ready, waiting..."
sleep 2
done
echo "$service_name is now running!"Waiting for File
#!/bin/bash
target_file="/tmp/ready.flag"
echo "Waiting for file $target_file to appear..."
until [ -f "$target_file" ]; do
echo "File not found, waiting..."
sleep 1
done
echo "File found! Continuing..."Loop Control Statements
break Statement
Exits the loop immediately:
#!/bin/bash
# Find first .txt file
for file in *; do
if [[ "$file" == *.txt ]]; then
echo "Found text file: $file"
break
fi
echo "Checking: $file"
donecontinue Statement
Skips the rest of the current iteration:
#!/bin/bash
# Process only .txt files
for file in *; do
# Skip non-txt files
if [[ "$file" != *.txt ]]; then
continue
fi
echo "Processing text file: $file"
wc -l "$file"
doneNested Loops with break and continue
#!/bin/bash
# Nested loops example
for i in {1..3}; do
echo "Outer loop: $i"
for j in {1..5}; do
if [ $j -eq 3 ]; then
echo " Skipping inner loop iteration $j"
continue
fi
if [ $i -eq 2 ] && [ $j -eq 4 ]; then
echo " Breaking inner loop at $i,$j"
break
fi
echo " Inner loop: $j"
done
donePractical Loop Examples
Batch File Processing
#!/bin/bash
# Batch resize images
image_dir="./images"
output_dir="./thumbnails"
# Create output directory
mkdir -p "$output_dir"
for image in "$image_dir"/*.{jpg,jpeg,png}; do
# Skip if no files match
[ ! -f "$image" ] && continue
filename=$(basename "$image")
name="${filename%.*}"
extension="${filename##*.}"
echo "Processing: $filename"
# Resize image (requires ImageMagick)
if command -v convert >/dev/null 2>&1; then
convert "$image" -resize 200x200 "$output_dir/${name}_thumb.$extension"
echo "Created thumbnail: ${name}_thumb.$extension"
else
echo "ImageMagick not installed, skipping resize"
fi
doneLog Analysis
#!/bin/bash
log_file="/var/log/apache2/access.log"
declare -A ip_count
# Count IP addresses in log file
while IFS= read -r line; do
# Extract IP address (first field)
ip=$(echo "$line" | awk '{print $1}')
# Skip invalid IPs
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
((ip_count[$ip]++))
fi
done < "$log_file"
# Display top 10 IPs
echo "Top 10 IP addresses:"
for ip in "${!ip_count[@]}"; do
echo "$ip: ${ip_count[$ip]}"
done | sort -k2 -nr | head -10System Backup Script
#!/bin/bash
# Directories to backup
backup_dirs=("/home/user/documents" "/home/user/projects" "/etc")
backup_destination="/backup/$(date +%Y%m%d)"
log_file="/var/log/backup.log"
# Create backup destination
mkdir -p "$backup_destination"
# Function to log messages
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$log_file"
}
log_message "Starting backup process"
# Backup each directory
for dir in "${backup_dirs[@]}"; do
if [ -d "$dir" ]; then
dir_name=$(basename "$dir")
log_message "Backing up $dir"
if tar -czf "$backup_destination/${dir_name}.tar.gz" "$dir"; then
log_message "Successfully backed up $dir"
else
log_message "ERROR: Failed to backup $dir"
fi
else
log_message "WARNING: Directory $dir does not exist"
fi
done
log_message "Backup process completed"
# Clean up old backups (keep only last 7 days)
find /backup -name "20*" -type d -mtime +7 -exec rm -rf {} \;
log_message "Cleaned up old backups"User Management Script
#!/bin/bash
# Create multiple users from a list
users_file="users.txt"
# Create sample users file
cat > "$users_file" << EOF
john:John Doe:developer
jane:Jane Smith:admin
bob:Bob Johnson:user
EOF
# Process each user
while IFS=':' read -r username fullname role; do
# Skip empty lines and comments
[[ -z "$username" || "$username" =~ ^# ]] && continue
echo "Processing user: $username"
# Check if user already exists
if id "$username" >/dev/null 2>&1; then
echo "User $username already exists, skipping"
continue
fi
# Create user
if useradd -m -c "$fullname" "$username"; then
echo "Created user: $username ($fullname)"
# Set initial password
echo "$username:temp123" | chpasswd
echo "Set temporary password for $username"
# Add to appropriate group based on role
case "$role" in
"admin")
usermod -aG sudo "$username"
echo "Added $username to sudo group"
;;
"developer")
usermod -aG developers "$username" 2>/dev/null || echo "developers group not found"
;;
esac
else
echo "Failed to create user: $username"
fi
done < "$users_file"Loop Performance Tips
Avoid Unnecessary Command Substitution
# Slow - calls external command in each iteration
for i in {1..1000}; do
date_str=$(date)
echo "$i: $date_str"
done
# Fast - call command once
date_str=$(date)
for i in {1..1000}; do
echo "$i: $date_str"
doneUse Built-in Commands When Possible
# Slow - external command
for file in *.txt; do
lines=$(wc -l < "$file")
echo "$file: $lines lines"
done
# Faster - but still external command per file
for file in *.txt; do
while IFS= read -r line; do
((count++))
done < "$file"
echo "$file: $count lines"
count=0
doneProcess Files Efficiently
# Process multiple files with one command
find . -name "*.log" -print0 | while IFS= read -r -d '' file; do
echo "Processing: $file"
# Process file
doneCommon Loop Patterns
Retry Pattern
retry_command() {
local max_attempts=3
local attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt of $max_attempts"
if command_that_might_fail; then
echo "Command succeeded"
return 0
fi
echo "Command failed, retrying..."
((attempt++))
sleep 2
done
echo "Command failed after $max_attempts attempts"
return 1
}Progress Indicator
#!/bin/bash
total_items=100
for ((i=1; i<=total_items; i++)); do
# Simulate work
sleep 0.1
# Calculate progress
progress=$((i * 100 / total_items))
# Display progress bar
printf "\rProgress: [%-50s] %d%%" $(printf "#%.0s" $(seq 1 $((progress/2)))) $progress
done
echo
echo "Complete!"Loops are powerful constructs that enable automation and batch processing in your shell scripts. Understanding when and how to use each type of loop will help you write more efficient and effective scripts.