Skip to content

Loops

Loops are essential for repetitive tasks in bash scripting. This chapter covers the three main types of loops: for loops (both classic and C-style), while loops, and until loops. We’ll explore practical applications for DevOps workflows including file processing, command iteration, and monitoring tasks.


for_basic.sh
#!/usr/bin/env bash
# Iterate over a list of values
for fruit in apple banana orange; do
echo "Fruit: $fruit"
done
# Output:
# Fruit: apple
# Fruit: banana
# Fruit: orange
for_array.sh
#!/usr/bin/env bash
fruits=("apple" "banana" "orange" "mango")
# Iterate over array elements
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# With index
for i in "${!fruits[@]}"; do
echo "Index $i: ${fruits[$i]}"
done
for_files.sh
#!/usr/bin/env bash
# Iterate over files in directory
for file in /tmp/*.txt; do
echo "File: $file"
done
# Recursive (using find)
for file in $(find /tmp -name "*.txt" 2>/dev/null); do
echo "Found: $file"
done
# Safe file iteration (nullglob enabled)
shopt -s nullglob
for file in /tmp/*.txt; do
[ -f "$file" ] && echo "Processing: $file"
done
for_cstyle.sh
#!/usr/bin/env bash
# C-style for loop
for ((i=0; i<5; i++)); do
echo "Count: $i"
done
# With step
for ((i=0; i<10; i+=2)); do
echo "Even: $i"
done
# Reverse
for ((i=5; i>0; i--)); do
echo "Countdown: $i"
done
for_command.sh
#!/usr/bin/env bash
# Iterate over command output
echo "Users currently logged in:"
for user in $(who | awk '{print $1}' | sort -u); do
echo " - $user"
done
# Iterate over directories
echo "Top-level directories in /:"
for dir in /*/; do
echo "Directory: $dir"
done
# Iterate over git branches
for branch in $(git branch 2>/dev/null | sed 's/^\*\? //'); do
echo "Branch: $branch"
done

while_basic.sh
#!/usr/bin/env bash
# Basic while loop
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
while_read_file.sh
#!/usr/bin/env bash
# Read file line by line
while IFS= read -r line; do
echo "Line: $line"
done < /etc/passwd
# Or with variable
file="/etc/passwd"
while IFS= read -r line; do
echo "$line"
done < "$file"
while_conditions.sh
#!/usr/bin/env bash
# While with multiple conditions
count=0
max=10
while [ $count -lt $max ] && [ $((count % 2)) -eq 0 ]; do
echo "Even count: $count"
count=$((count + 2))
done
while_infinite.sh
#!/usr/bin/env bash
# Infinite loop with break
counter=0
while true; do
echo "Iteration: $counter"
counter=$((counter + 1))
if [ $counter -ge 5 ]; then
echo "Breaking out of loop"
break
fi
done

until_basic.sh
#!/usr/bin/env bash
# Until loop - runs until condition is true
count=0
until [ $count -ge 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
until_vs_while.sh
#!/usr/bin/env bash
# While: runs while condition is true
count=0
while [ $count -lt 3 ]; do
echo "While: count is $count"
count=$((count + 1))
done
echo "---"
# Until: runs until condition becomes true
count=0
until [ $count -ge 3 ]; do
echo "Until: count is $count"
count=$((count + 1))
done

break_example.sh
#!/usr/bin/env bash
# Break out of loop based on condition
for i in {1..10}; do
if [ $i -eq 5 ]; then
echo "Breaking at $i"
break
fi
echo "Processing: $i"
done
continue_example.sh
#!/usr/bin/env bash
# Skip iteration based on condition
for i in {1..10}; do
if [ $((i % 2)) -eq 0 ]; then
continue # Skip even numbers
fi
echo "Odd number: $i"
done

ssh_servers.sh
#!/usr/bin/env bash
# List of servers
servers=(
"web01.example.com"
"web02.example.com"
"db01.example.com"
)
# SSH to each server
for server in "${servers[@]}"; do
echo "Connecting to $server..."
ssh -o ConnectTimeout=5 "$server" "hostname && uptime"
done
batch_process.sh
#!/usr/bin/env bash
# Process all .log files in directory
log_dir="/var/log"
output_dir="/tmp/processed"
mkdir -p "$output_dir"
for logfile in "$log_dir"/*.log; do
[ -f "$logfile" ] || continue
filename=$(basename "$logfile")
echo "Processing: $filename"
# Process each log file
# Count lines, errors, warnings
lines=$(wc -l < "$logfile")
errors=$(grep -c "ERROR" "$logfile" 2>/dev/null || echo 0)
warnings=$(grep -c "WARN" "$logfile" 2>/dev/null || echo 0)
echo "$filename: $lines lines, $errors errors, $warnings warnings"
# Archive processed file
gzip -c "$logfile" > "$output_dir/${filename}.gz"
done
echo "Processing complete"
monitor_loop.sh
#!/usr/bin/env bash
set -euo pipefail
# Monitor service health
check_service() {
local service="$1"
if systemctl is-active --quiet "$service"; then
echo "[$(date '+%H:%M:%S')] $service: OK"
return 0
else
echo "[$(date '+%H:%M:%S')] $service: FAILED"
return 1
fi
}
# Monitor for specified duration
MONITOR_DURATION=60 # seconds
CHECK_INTERVAL=5 # seconds
services=("nginx" "postgresql" "redis")
end_time=$((SECONDS + MONITOR_DURATION))
echo "Starting health monitoring for ${MONITOR_DURATION}s..."
echo "Services: ${services[*]}"
echo "---"
while [ $SECONDS -lt $end_time ]; do
all_healthy=true
for service in "${services[@]}"; do
if ! check_service "$service"; then
all_healthy=false
# Optional: Send alert here
fi
done
if [ "$all_healthy" = true ]; then
echo "--- All services healthy ---"
else
echo "--- Some services failed ---"
fi
sleep $CHECK_INTERVAL
done
echo "Monitoring complete"
db_backup.sh
#!/usr/bin/env bash
set -euo pipefail
# Configuration
BACKUP_DIR="/backups"
DATABASES=("users" "orders" "products")
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
# Function to backup database
backup_db() {
local db="$1"
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="${BACKUP_DIR}/${db}_${timestamp}.sql"
echo "Backing up database: $db"
# Simulate database backup (replace with actual backup command)
# mysqldump -u root -p"$DB_PASSWORD" "$db" > "$backup_file"
echo "CREATE DATABASE IF NOT EXISTS $db;" > "$backup_file"
if [ -f "$backup_file" ]; then
echo "Backup complete: $backup_file"
return 0
else
echo "Backup failed for: $db"
return 1
fi
}
# Backup all databases
echo "Starting database backups..."
echo "Backup directory: $BACKUP_DIR"
echo "Databases: ${DATABASES[*]}"
echo "---"
for db in "${DATABASES[@]}"; do
backup_db "$db"
done
echo "---"
echo "All backups complete"
# Clean old backups
echo "Cleaning old backups (older than $RETENTION_DAYS days)..."
find "$BACKUP_DIR" -name "*.sql" -mtime +$RETENTION_DAYS -delete
echo "Done"
container_manager.sh
#!/usr/bin/env bash
set -euo pipefail
# Get all running containers
containers=$(docker ps --format '{{.Names}}')
if [ -z "$containers" ]; then
echo "No running containers found"
exit 0
fi
echo "Found ${#containers[@]} running containers"
echo "---"
# Stop all containers
for container in $containers; do
echo "Stopping: $container"
docker stop "$container"
done
echo "---"
echo "All containers stopped"
# Optionally start them back
if [ "${1:-}" = "--restart" ]; then
echo "Starting containers back..."
docker start $containers
echo "All containers started"
fi
create_users.sh
#!/usr/bin/env bash
set -euo pipefail
# Array of users to create
users=(
"john:John Doe:/bin/bash"
"jane:Jane Doe:/bin/zsh"
"bob:Bob Smith:/bin/bash"
)
# Create users
for user_entry in "${users[@]}"; do
IFS=':' read -r username fullname shell <<< "$user_entry"
echo "Creating user: $username"
# Check if user exists
if id "$username" &>/dev/null; then
echo " User $username already exists, skipping"
continue
fi
# Create user
useradd -m -c "$fullname" -s "$shell" "$username"
echo " User $username created"
done
echo "User creation complete"

loop_assoc_array.sh
#!/usr/bin/env bash
# Declare associative array
declare -A server_info=(
[web01]="192.168.1.10"
[web02]="192.168.1.11"
[db01]="192.168.1.20"
)
# Iterate over keys
echo "Server list:"
for server in "${!server_info[@]}"; do
ip="${server_info[$server]}"
echo " $server -> $ip"
done
# Sort by key
echo "Sorted:"
for server in $(echo "${!server_info[@]}" | tr ' ' '\n' | sort); do
echo " $server -> ${server_info[$server]}"
done
process_csv.sh
#!/usr/bin/env bash
# Sample CSV data
CSV_DATA="name,email,role
John,john@example.com,admin
Jane,jane@example.com,developer
Bob,bob@example.com,devops"
# Process CSV
echo "$CSV_DATA" | tail -n +2 | while IFS=',' read -r name email role; do
echo "Processing: $name ($role)"
echo " Email: $email"
done

performance.sh
#!/usr/bin/env bash
# SLOW: External command in loop
for i in {1..1000}; do
echo "$i" > /dev/null
done
# FASTER: Built-in operations
for i in {1..1000}; do
:
done
# Use pipes carefully
# Slower - creates subshell for each iteration
for file in *.txt; do
wc -l "$file"
done
# Faster - process in single subshell
wc -l *.txt
# Use process substitution instead of command substitution
while read -r line; do
echo "$line"
done < <(command)

timeout_loop.sh
#!/usr/bin/env bash
# Run loop with timeout
timeout_seconds=30
start_time=$(date +%s)
while true; do
current_time=$(date +%s)
elapsed=$((current_time - start_time))
if [ $elapsed -ge $timeout_seconds ]; then
echo "Timeout reached after $elapsed seconds"
break
fi
echo "Running... (${elapsed}s)"
sleep 2
done

In this chapter, you learned about:

  • ✅ Basic for loops
  • ✅ For loops over arrays
  • ✅ For loops over files
  • ✅ C-style for loops
  • ✅ For loops with command output
  • ✅ While loops
  • ✅ Reading files line by line
  • ✅ Until loops
  • ✅ Break and continue statements
  • ✅ Practical DevOps examples
  • ✅ Loops with associative arrays
  • ✅ Performance considerations

  1. Write a for loop that prints numbers 1 to 10
  2. Create a while loop that reads a file line by line
  3. Write a script that iterates over command-line arguments
  1. Create a script that backs up all databases in a loop
  2. Write a monitoring script that checks service health in a loop
  3. Implement a container restart script using loops
  1. Create a script that processes log files with multiple conditions
  2. Implement a concurrent processing script
  3. Build a retry mechanism with exponential backoff using loops

Continue to the next chapter to learn about loop control statements.


Previous Chapter: Case Statements Next Chapter: Loop Control