Loops
Chapter 9: Loops (for, while, until)
Section titled “Chapter 9: Loops (for, while, until)”Overview
Section titled “Overview”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 Loops
Section titled “For Loops”Basic For Loop
Section titled “Basic For Loop”#!/usr/bin/env bash# Iterate over a list of valuesfor fruit in apple banana orange; do echo "Fruit: $fruit"done
# Output:# Fruit: apple# Fruit: banana# Fruit: orangeFor Loop Over Array
Section titled “For Loop Over Array”#!/usr/bin/env bashfruits=("apple" "banana" "orange" "mango")
# Iterate over array elementsfor fruit in "${fruits[@]}"; do echo "Fruit: $fruit"done
# With indexfor i in "${!fruits[@]}"; do echo "Index $i: ${fruits[$i]}"doneFor Loop Over Files
Section titled “For Loop Over Files”#!/usr/bin/env bash# Iterate over files in directoryfor 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 nullglobfor file in /tmp/*.txt; do [ -f "$file" ] && echo "Processing: $file"doneC-Style For Loop
Section titled “C-Style For Loop”#!/usr/bin/env bash# C-style for loopfor ((i=0; i<5; i++)); do echo "Count: $i"done
# With stepfor ((i=0; i<10; i+=2)); do echo "Even: $i"done
# Reversefor ((i=5; i>0; i--)); do echo "Countdown: $i"doneFor Loop with Command Output
Section titled “For Loop with Command Output”#!/usr/bin/env bash# Iterate over command outputecho "Users currently logged in:"for user in $(who | awk '{print $1}' | sort -u); do echo " - $user"done
# Iterate over directoriesecho "Top-level directories in /:"for dir in /*/; do echo "Directory: $dir"done
# Iterate over git branchesfor branch in $(git branch 2>/dev/null | sed 's/^\*\? //'); do echo "Branch: $branch"doneWhile Loops
Section titled “While Loops”Basic While Loop
Section titled “Basic While Loop”#!/usr/bin/env bash# Basic while loopcount=0
while [ $count -lt 5 ]; do echo "Count: $count" count=$((count + 1))doneRead File Line by Line
Section titled “Read File Line by Line”#!/usr/bin/env bash# Read file line by linewhile IFS= read -r line; do echo "Line: $line"done < /etc/passwd
# Or with variablefile="/etc/passwd"while IFS= read -r line; do echo "$line"done < "$file"While with Multiple Conditions
Section titled “While with Multiple Conditions”#!/usr/bin/env bash# While with multiple conditionscount=0max=10
while [ $count -lt $max ] && [ $((count % 2)) -eq 0 ]; do echo "Even count: $count" count=$((count + 2))doneInfinite While Loop
Section titled “Infinite While Loop”#!/usr/bin/env bash# Infinite loop with breakcounter=0
while true; do echo "Iteration: $counter" counter=$((counter + 1))
if [ $counter -ge 5 ]; then echo "Breaking out of loop" break fidoneUntil Loops
Section titled “Until Loops”Basic Until Loop
Section titled “Basic Until Loop”#!/usr/bin/env bash# Until loop - runs until condition is truecount=0
until [ $count -ge 5 ]; do echo "Count: $count" count=$((count + 1))doneUntil vs While
Section titled “Until vs While”#!/usr/bin/env bash# While: runs while condition is truecount=0while [ $count -lt 3 ]; do echo "While: count is $count" count=$((count + 1))done
echo "---"
# Until: runs until condition becomes truecount=0until [ $count -ge 3 ]; do echo "Until: count is $count" count=$((count + 1))doneLoop Control
Section titled “Loop Control”Break Statement
Section titled “Break Statement”#!/usr/bin/env bash# Break out of loop based on conditionfor i in {1..10}; do if [ $i -eq 5 ]; then echo "Breaking at $i" break fi echo "Processing: $i"doneContinue Statement
Section titled “Continue Statement”#!/usr/bin/env bash# Skip iteration based on conditionfor i in {1..10}; do if [ $((i % 2)) -eq 0 ]; then continue # Skip even numbers fi echo "Odd number: $i"donePractical Examples
Section titled “Practical Examples”Example 1: Process Multiple Servers
Section titled “Example 1: Process Multiple Servers”#!/usr/bin/env bash# List of serversservers=( "web01.example.com" "web02.example.com" "db01.example.com")
# SSH to each serverfor server in "${servers[@]}"; do echo "Connecting to $server..." ssh -o ConnectTimeout=5 "$server" "hostname && uptime"doneExample 2: Batch File Processing
Section titled “Example 2: Batch File Processing”#!/usr/bin/env bash# Process all .log files in directorylog_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"Example 3: Health Monitoring Loop
Section titled “Example 3: Health Monitoring Loop”#!/usr/bin/env bashset -euo pipefail
# Monitor service healthcheck_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 durationMONITOR_DURATION=60 # secondsCHECK_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_INTERVALdone
echo "Monitoring complete"Example 4: Database Backup Loop
Section titled “Example 4: Database Backup Loop”#!/usr/bin/env bashset -euo pipefail
# ConfigurationBACKUP_DIR="/backups"DATABASES=("users" "orders" "products")RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
# Function to backup databasebackup_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 databasesecho "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 backupsecho "Cleaning old backups (older than $RETENTION_DAYS days)..."find "$BACKUP_DIR" -name "*.sql" -mtime +$RETENTION_DAYS -delete
echo "Done"Example 5: Container Management
Section titled “Example 5: Container Management”#!/usr/bin/env bashset -euo pipefail
# Get all running containerscontainers=$(docker ps --format '{{.Names}}')
if [ -z "$containers" ]; then echo "No running containers found" exit 0fi
echo "Found ${#containers[@]} running containers"echo "---"
# Stop all containersfor container in $containers; do echo "Stopping: $container" docker stop "$container"done
echo "---"echo "All containers stopped"
# Optionally start them backif [ "${1:-}" = "--restart" ]; then echo "Starting containers back..." docker start $containers echo "All containers started"fiExample 6: User Creation
Section titled “Example 6: User Creation”#!/usr/bin/env bashset -euo pipefail
# Array of users to createusers=( "john:John Doe:/bin/bash" "jane:Jane Doe:/bin/zsh" "bob:Bob Smith:/bin/bash")
# Create usersfor 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"Loops with Arrays and Associative Arrays
Section titled “Loops with Arrays and Associative Arrays”Iterating Over Associative Arrays
Section titled “Iterating Over Associative Arrays”#!/usr/bin/env bash# Declare associative arraydeclare -A server_info=( [web01]="192.168.1.10" [web02]="192.168.1.11" [db01]="192.168.1.20")
# Iterate over keysecho "Server list:"for server in "${!server_info[@]}"; do ip="${server_info[$server]}" echo " $server -> $ip"done
# Sort by keyecho "Sorted:"for server in $(echo "${!server_info[@]}" | tr ' ' '\n' | sort); do echo " $server -> ${server_info[$server]}"doneProcessing CSV Data
Section titled “Processing CSV Data”#!/usr/bin/env bash# Sample CSV dataCSV_DATA="name,email,roleJohn,john@example.com,adminJane,jane@example.com,developerBob,bob@example.com,devops"
# Process CSVecho "$CSV_DATA" | tail -n +2 | while IFS=',' read -r name email role; do echo "Processing: $name ($role)" echo " Email: $email"donePerformance Considerations
Section titled “Performance Considerations”Use Native Bash When Possible
Section titled “Use Native Bash When Possible”#!/usr/bin/env bash# SLOW: External command in loopfor i in {1..1000}; do echo "$i" > /dev/nulldone
# FASTER: Built-in operationsfor i in {1..1000}; do :done
# Use pipes carefully# Slower - creates subshell for each iterationfor file in *.txt; do wc -l "$file"done
# Faster - process in single subshellwc -l *.txt
# Use process substitution instead of command substitutionwhile read -r line; do echo "$line"done < <(command)Infinite Loops with Timeout
Section titled “Infinite Loops with Timeout”#!/usr/bin/env bash# Run loop with timeouttimeout_seconds=30start_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 2doneSummary
Section titled “Summary”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
Exercises
Section titled “Exercises”Level 1: Basics
Section titled “Level 1: Basics”- Write a for loop that prints numbers 1 to 10
- Create a while loop that reads a file line by line
- Write a script that iterates over command-line arguments
Level 2: Intermediate
Section titled “Level 2: Intermediate”- Create a script that backs up all databases in a loop
- Write a monitoring script that checks service health in a loop
- Implement a container restart script using loops
Level 3: Advanced
Section titled “Level 3: Advanced”- Create a script that processes log files with multiple conditions
- Implement a concurrent processing script
- Build a retry mechanism with exponential backoff using loops
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about loop control statements.
Previous Chapter: Case Statements Next Chapter: Loop Control