Introduction to Bash Loops
Loops are fundamental control structures in Bash scripting that allow you to execute commands repeatedly. They are essential for automating repetitive tasks, processing files, iterating through data, and controlling program flow. Bash provides several types of loops, each suited for different scenarios.
Key Concepts
- Iteration: Repeating a set of commands multiple times
- Loop Control: Managing loop execution with conditions
- Loop Types:
for,while,until, andselectloops - Loop Control Statements:
breakandcontinue - Loop Performance: Different loops have different performance characteristics
1. For Loops
Basic For Loop Syntax
#!/bin/bash # Basic for loop with list for fruit in apple banana orange; do echo "I like $fruit" done # Output: # I like apple # I like banana # I like orange # For loop with variable list items="one two three" for item in $items; do echo "Item: $item" done # For loop with wildcards for file in *.txt; do echo "Text file: $file" done
C-style For Loop
#!/bin/bash # C-style for loop for (( i=1; i<=5; i++ )); do echo "Iteration $i" done # Multiple variables for (( i=0, j=10; i<=10; i++, j-- )); do echo "i=$i, j=$j" done # Decrement loop for (( i=10; i>=1; i-- )); do echo "Countdown: $i" done # Step by 2 for (( i=0; i<=10; i+=2 )); do echo "Even: $i" done
For Loop with Ranges
#!/bin/bash # Brace expansion ranges for i in {1..5}; do echo "Number $i" done # With step (Bash 4+) for i in {1..10..2}; do echo "Odd: $i" done # Reverse range for i in {10..1}; do echo "Reverse: $i" done # Alphabetic ranges for letter in {a..e}; do echo "Letter: $letter" done for letter in {A..Z}; do echo -n "$letter " done echo For Loop with Command Substitution
#!/bin/bash # Loop through command output for file in $(ls *.txt 2>/dev/null); do echo "Processing $file" wc -l "$file" done # Loop through lines from file for line in $(cat file.txt); do echo "Line: $line" done # Better way: read lines with IFS IFS=$'\n' for line in $(cat file.txt); do echo "Line: $line" done # Loop through find results for file in $(find . -name "*.sh" -type f); do echo "Shell script: $file" chmod +x "$file" done
For Loop with Arrays
#!/bin/bash # Indexed array fruits=("apple" "banana" "orange" "grape") # Loop through all elements for fruit in "${fruits[@]}"; do echo "Fruit: $fruit" done # Loop with index for i in "${!fruits[@]}"; do echo "Index $i: ${fruits[$i]}" done # Associative array (Bash 4+) declare -A capitals capitals=( ["USA"]="Washington" ["France"]="Paris" ["Japan"]="Tokyo" ) for country in "${!capitals[@]}"; do echo "$country: ${capitals[$country]}" done # Modify array elements numbers=(1 2 3 4 5) for i in "${!numbers[@]}"; do numbers[$i]=$((numbers[$i] * 2)) done echo "${numbers[@]}" # 2 4 6 8 10 2. While Loops
Basic While Loop
#!/bin/bash # Basic while loop count=1 while [ $count -le 5 ]; do echo "Count: $count" ((count++)) done # Infinite loop (Ctrl+C to stop) while true; do echo "Running forever..." sleep 1 done # Loop with condition at end while : ; do echo "Infinite loop with :" sleep 1 done
While Loop with File Reading
#!/bin/bash # Read file line by line while IFS= read -r line; do echo "Line: $line" done < file.txt # Read file with line numbers line_num=1 while IFS= read -r line; do echo "$line_num: $line" ((line_num++)) done < file.txt # Process CSV file while IFS=',' read -r name age city; do echo "Name: $name, Age: $age, City: $city" done < data.csv # Read from command output while IFS= read -r line; do echo "Process: $line" done < <(ps aux) # Read multiple files while IFS= read -r line1 && IFS= read -r line2 <&3; do echo "File1: $line1" echo "File2: $line2" echo "---" done < file1.txt 3< file2.txt
While Loop with User Input
#!/bin/bash # Read user input until quit while true; do read -p "Enter command (quit to exit): " command if [ "$command" = "quit" ]; then break fi echo "You entered: $command" done # Validate input while [ -z "$name" ]; do read -p "Please enter your name: " name done echo "Hello, $name!" # Menu system while true; do echo "1. Option 1" echo "2. Option 2" echo "3. Quit" read -p "Choose: " choice case $choice in 1) echo "Option 1 selected" ;; 2) echo "Option 2 selected" ;; 3) break ;; *) echo "Invalid option" ;; esac done
While Loop with Process Control
#!/bin/bash # Monitor process process="myapp" while pgrep -x "$process" >/dev/null; do echo "$process is still running..." sleep 5 done echo "$process has terminated" # Wait for file to appear while [ ! -f "/tmp/ready.txt" ]; do echo "Waiting for ready file..." sleep 2 done echo "Ready file detected!" # Countdown timer seconds=10 while [ $seconds -gt 0 ]; do echo -ne "Time remaining: $seconds seconds\r" sleep 1 ((seconds--)) done echo -e "\nTime's up!"
3. Until Loops
Basic Until Loop
#!/bin/bash # Until loop (opposite of while) count=1 until [ $count -gt 5 ]; do echo "Count: $count" ((count++)) done # Wait for condition until ping -c1 google.com &>/dev/null; do echo "Waiting for network..." sleep 2 done echo "Network is up!" # Until file exists until [ -f "/tmp/stop.txt" ]; do echo "Processing... (create /tmp/stop.txt to stop)" # Do work sleep 1 done echo "Stop file detected, exiting"
Until Loop Examples
#!/bin/bash # Retry until success until ./my_script.sh; do echo "Script failed, retrying in 5 seconds..." sleep 5 done # Wait for service until curl -s http://localhost:8080/health >/dev/null; do echo "Waiting for service to start..." sleep 2 done echo "Service is ready!" # Until user enters correct password password="secret123" input="" until [ "$input" = "$password" ]; do read -s -p "Enter password: " input echo if [ "$input" != "$password" ]; then echo "Incorrect password, try again" fi done echo "Access granted!" # Until condition met attempt=1 max_attempts=5 until [ $attempt -gt $max_attempts ]; do echo "Attempt $attempt of $max_attempts" if some_command; then echo "Success!" break fi ((attempt++)) done if [ $attempt -gt $max_attempts ]; then echo "All attempts failed" fi
4. Select Loops
Basic Select Loop
#!/bin/bash # Simple menu with select echo "Choose your favorite color:" select color in Red Green Blue Yellow Quit; do case $color in Red|Green|Blue|Yellow) echo "You chose $color" ;; Quit) echo "Goodbye!" break ;; *) echo "Invalid choice $REPLY" ;; esac done # Select with custom prompt PS3="Please select an option: " options=("Option 1" "Option 2" "Option 3" "Exit") select opt in "${options[@]}"; do case $opt in "Option 1") echo "You selected option 1" ;; "Option 2") echo "You selected option 2" ;; "Option 3") echo "You selected option 3" ;; "Exit") echo "Exiting..." break ;; *) echo "Invalid option $REPLY" ;; esac done Advanced Select Examples
#!/bin/bash # Dynamic menu from array servers=("Web Server" "Database Server" "File Server" "Backup Server" "Exit") PS3="Select server to manage: " select server in "${servers[@]}"; do case $server in "Web Server") echo "Managing Web Server..." # Add web server management commands ;; "Database Server") echo "Managing Database Server..." # Add database server management commands ;; "File Server") echo "Managing File Server..." # Add file server management commands ;; "Backup Server") echo "Managing Backup Server..." # Add backup server management commands ;; "Exit") echo "Exiting server management" break ;; *) echo "Invalid selection: $REPLY" ;; esac done # Menu with submenus main_menu() { PS3="Main menu: " select main in "Submenu 1" "Submenu 2" "Exit"; do case $main in "Submenu 1") submenu1 ;; "Submenu 2") submenu2 ;; "Exit") break 2 ;; esac done } submenu1() { PS3="Submenu 1: " select sub in "Action A" "Action B" "Back"; do case $sub in "Action A") echo "Performing Action A" ;; "Action B") echo "Performing Action B" ;; "Back") break ;; esac done } submenu2() { PS3="Submenu 2: " select sub in "Action X" "Action Y" "Back"; do case $sub in "Action X") echo "Performing Action X" ;; "Action Y") echo "Performing Action Y" ;; "Back") break ;; esac done } # Call main menu main_menu 5. Loop Control Statements
Break Statement
#!/bin/bash # Break out of loop for i in {1..10}; do if [ $i -eq 5 ]; then echo "Breaking at $i" break fi echo "Number: $i" done # Break with labels outer_loop: for i in {1..3}; do echo "Outer loop: $i" for j in {1..3}; do if [ $i -eq 2 ] && [ $j -eq 2 ]; then echo "Breaking outer loop" break outer_loop fi echo " Inner loop: $j" done done # Break from infinite loop count=0 while true; do ((count++)) if [ $count -gt 10 ]; then break fi echo "Count: $count" done Continue Statement
#!/bin/bash # Skip odd numbers for i in {1..10}; do if [ $((i % 2)) -eq 1 ]; then continue fi echo "Even: $i" done # Skip specific files for file in *; do if [ "$file" = "skip.txt" ] || [ "$file" = "temp.txt" ]; then continue fi echo "Processing: $file" done # Continue with labels for i in {1..3}; do echo "Outer: $i" for j in {1..3}; do if [ $j -eq 2 ]; then continue 2 # Continue outer loop fi echo " Inner: $j" done done Exit Statement
#!/bin/bash # Exit script from loop for file in *; do if [ ! -r "$file" ]; then echo "Error: Cannot read $file" exit 1 fi echo "Processing $file" done # Exit with specific code check_files() { local dir="$1" cd "$dir" || exit 2 for file in *; do if [ ! -f "$file" ]; then echo "Not a regular file: $file" exit 3 fi done } 6. Nested Loops
Basic Nested Loops
#!/bin/bash # Multiplication table for i in {1..5}; do for j in {1..5}; do printf "%4d" $((i * j)) done echo done # Triangle pattern rows=5 for ((i=1; i<=rows; i++)); do for ((j=1; j<=i; j++)); do echo -n "* " done echo done # 2D array traversal matrix=( "1 2 3" "4 5 6" "7 8 9" ) for row in "${matrix[@]}"; do for value in $row; do echo -n "$value " done echo done Complex Nested Loops
#!/bin/bash # Find pairs that sum to target target=10 for i in {1..9}; do for j in {1..9}; do if [ $((i + j)) -eq $target ]; then echo "$i + $j = $target" fi done done # Process files by directory for dir in */; do echo "Directory: ${dir%/}" for file in "$dir"*.txt; do if [ -f "$file" ]; then lines=$(wc -l < "$file") echo " $file: $lines lines" fi done done # Time-based scheduling for hour in {0..23}; do for minute in {0..59..15}; do printf "Schedule: %02d:%02d\n" $hour $minute done done 7. Loop Control with IFS
IFS Examples
#!/bin/bash # Default IFS (space, tab, newline) data="apple banana cherry" for fruit in $data; do echo "$fruit" done # Custom IFS for CSV data="John,25,NYC;Jane,30,LA;Bob,35,Chicago" IFS=';' read -ra records <<< "$data" for record in "${records[@]}"; do IFS=',' read -r name age city <<< "$record" echo "Name: $name, Age: $age, City: $city" done # IFS for path splitting PATH="/usr/local/bin:/usr/bin:/bin" IFS=':' read -ra paths <<< "$PATH" for dir in "${paths[@]}"; do echo "Path directory: $dir" done # Preserve leading/trailing spaces data=" leading trailing " IFS= read -r preserved <<< "$data" echo "Preserved: '$preserved'" # Multi-line with IFS data="line1 line2 line3" IFS=$'\n' read -ra lines <<< "$data" for line in "${lines[@]}"; do echo "Line: $line" done 8. Loop Optimization
Performance Considerations
#!/bin/bash # Bad: Command substitution in loop condition for i in $(seq 1 1000); do # ... do work done # Better: Brace expansion for i in {1..1000}; do # ... do work done # Best: C-style for loop for ((i=1; i<=1000; i++)); do # ... do work done # Bad: Repeated external commands for file in *.txt; do lines=$(wc -l < "$file") echo "$file: $lines" done # Better: Use built-in operations for file in *.txt; do lines=$(grep -c '^' "$file") # Still external echo "$file: $lines" done # Bad: Reading file line by line while IFS= read -r line; do echo "$line" done < hugefile.txt # Better for simple operations cat hugefile.txt # Use arrays for speed files=() for file in *.txt; do files+=("$file") done # Process in chunks process_chunk() { local chunk=("${@}") for item in "${chunk[@]}"; do # Process item done } chunk_size=10 for ((i=0; i<${#files[@]}; i+=chunk_size)); do chunk=("${files[@]:i:chunk_size}") process_chunk "${chunk[@]}" & done wait Loop Optimizations
#!/bin/bash # Cache expensive operations files=(*.txt) # Expand once for file in "${files[@]}"; do # Process file done # Avoid repeated calculations max=${#files[@]} for ((i=0; i<max; i++)); do file="${files[$i]}" # Process file done # Use built-in commands when possible # Instead of: lines=$(cat file | wc -l) lines=$(wc -l < file) # Parallel processing with background jobs process_file() { file="$1" # Process file } for file in *.txt; do process_file "$file" & # Limit concurrent jobs if [ $(jobs -r | wc -l) -ge 4 ]; then wait fi done wait 9. Practical Examples
File Processing
#!/bin/bash # Batch rename files counter=1 for file in *.jpg; do mv "$file" "image_$(printf "%03d" $counter).jpg" ((counter++)) done # Find and process files find . -type f -name "*.log" -print0 | while IFS= read -r -d '' file; do echo "Processing: $file" gzip "$file" done # Create numbered backups for file in *.conf; do cp "$file" "$file.$(date +%Y%m%d).bak" done # Check file permissions for file in *.sh; do if [ ! -x "$file" ]; then echo "Making $file executable" chmod +x "$file" fi done # Process CSV with header read -r header < data.csv IFS=',' read -ra columns <<< "$header" echo "Columns: ${columns[*]}" line_num=1 while IFS=',' read -ra values; do ((line_num++)) echo "Record $line_num:" for i in "${!columns[@]}"; do echo " ${columns[$i]}: ${values[$i]}" done done < data.csv System Administration
#!/bin/bash # Monitor processes processes=("httpd" "mysqld" "sshd") for proc in "${processes[@]}"; do if pgrep -x "$proc" >/dev/null; then echo "$proc is running" else echo "$proc is NOT running" fi done # Check disk usage for dir in /home/*; do if [ -d "$dir" ]; then usage=$(du -sh "$dir" | cut -f1) echo "$(basename "$dir"): $usage" fi done # Test network connectivity servers=("google.com" "github.com" "stackoverflow.com") for server in "${servers[@]}"; do if ping -c1 -W2 "$server" >/dev/null 2>&1; then echo "✓ $server is reachable" else echo "✗ $server is unreachable" fi done # Check service status services=("nginx" "postgresql" "redis") for service in "${services[@]}"; do if systemctl is-active --quiet "$service"; then echo "✓ $service is active" else echo "✗ $service is inactive" fi done Data Processing
#!/bin/bash # Calculate average numbers=(10 20 30 40 50) sum=0 count=0 for num in "${numbers[@]}"; do sum=$((sum + num)) ((count++)) done average=$((sum / count)) echo "Average: $average" # Find min and max min=${numbers[0]} max=${numbers[0]} for num in "${numbers[@]}"; do if [ "$num" -lt "$min" ]; then min=$num fi if [ "$num" -gt "$max" ]; then max=$num fi done echo "Min: $min, Max: $max" # Filter data for num in {1..20}; do if [ $((num % 3)) -eq 0 ] && [ $((num % 5)) -eq 0 ]; then echo "FizzBuzz: $num" elif [ $((num % 3)) -eq 0 ]; then echo "Fizz: $num" elif [ $((num % 5)) -eq 0 ]; then echo "Buzz: $num" fi done # Group by pattern declare -A groups for file in *; do ext="${file##*.}" groups["$ext"]=$((groups["$ext"] + 1)) done for ext in "${!groups[@]}"; do echo "*.${ext}: ${groups[$ext]} files" done Utility Scripts
#!/bin/bash # Progress bar progress_bar() { local current=$1 local total=$2 local width=50 local percent=$((current * 100 / total)) local completed=$((current * width / total)) local remaining=$((width - completed)) printf "\r[" printf "%${completed}s" | tr ' ' '#' printf "%${remaining}s" | tr ' ' '-' printf "] %d%%" $percent } total=100 for i in $(seq 1 $total); do progress_bar $i $total sleep 0.1 done echo # Countdown timer countdown() { local seconds=$1 while [ $seconds -gt 0 ]; do printf "\rTime remaining: %02d:%02d" $((seconds/60)) $((seconds%60)) sleep 1 ((seconds--)) done echo -e "\nTime's up!" } countdown 10 # Retry function retry() { local max_attempts=$1 local delay=$2 shift 2 local attempt=1 until "$@" || [ $attempt -ge $max_attempts ]; do echo "Attempt $attempt failed, retrying in ${delay}s..." sleep $delay ((attempt++)) done if [ $attempt -ge $max_attempts ]; then echo "All attempts failed" return 1 fi } retry 3 5 curl -f http://localhost:8080 10. Loop Patterns and Idioms
Common Loop Patterns
#!/bin/bash # Read until EOF while IFS= read -r line; do echo "$line" done # Process arguments for arg in "$@"; do echo "Argument: $arg" done # Process options while getopts "ab:c:" opt; do case $opt in a) echo "Option a" ;; b) echo "Option b with value: $OPTARG" ;; c) echo "Option c with value: $OPTARG" ;; *) echo "Invalid option" ;; esac done # Process here document while read -r line; do echo "Processing: $line" done << EOF Line 1 Line 2 Line 3 EOF # Process command output ps aux | while read -r line; do if [[ $line =~ ^USER ]]; then echo "Header: $line" else echo "Process: $line" fi done # Generate sequences for i in {1..5}; do for j in {1..5}; do echo -n "$((i * j)) " done echo done Loop with Case Statements
#!/bin/bash # Menu-driven script while true; do clear echo "=== System Manager ===" echo "1. Show disk usage" echo "2. Show memory info" echo "3. Show process info" echo "4. Exit" echo -n "Choice: " read choice case $choice in 1) df -h echo -n "Press Enter to continue..." read ;; 2) free -h echo -n "Press Enter to continue..." read ;; 3) ps aux | head -20 echo -n "Press Enter to continue..." read ;; 4) echo "Goodbye!" break ;; *) echo "Invalid choice" sleep 2 ;; esac done # Process files based on type for file in *; do case $file in *.sh) echo "Shell script: $file" ;; *.txt) echo "Text file: $file" ;; *.jpg|*.png|*.gif) echo "Image: $file" ;; *) echo "Other: $file" ;; esac done
Loop with Functions
#!/bin/bash # Function that uses loops process_files() { local pattern="$1" local action="$2" for file in $pattern; do if [ -f "$file" ]; then $action "$file" fi done } # Function that returns via loop find_files_by_size() { local min_size="$1" local max_size="$2" for file in *; do if [ -f "$file" ]; then size=$(stat -c%s "$file") if [ "$size" -ge "$min_size" ] && [ "$size" -le "$max_size" ]; then echo "$file" fi fi done } # Recursive function with loop scan_directory() { local dir="$1" local indent="$2" for item in "$dir"/*; do if [ -d "$item" ]; then echo "${indent}📁 $(basename "$item")" scan_directory "$item" " $indent" else echo "${indent}📄 $(basename "$item")" fi done } # Usage process_files "*.sh" "chmod +x" find_files_by_size 1000 10000 scan_directory "/home/user" "" 11. Error Handling in Loops
Loop Error Handling
#!/bin/bash # Set error handling set -e # Exit on error set -u # Exit on undefined variable # Error handling with trap trap 'echo "Error on line $LINENO"' ERR # Check each command for file in *.txt; do if ! cp "$file" "/backup/"; then echo "Failed to copy $file" >&2 continue fi if ! gzip "/backup/$file"; then echo "Failed to compress /backup/$file" >&2 continue fi done # Validate before processing for file in "$@"; do if [ ! -f "$file" ]; then echo "Error: $file is not a regular file" >&2 continue fi if [ ! -r "$file" ]; then echo "Error: $file is not readable" >&2 continue fi # Process file echo "Processing $file" done # Retry on failure max_retries=3 for i in {1..5}; do retry=0 until curl -f "http://example.com/api/data/$i" -o "data_$i.json"; do ((retry++)) if [ $retry -ge $max_retries ]; then echo "Failed to fetch data for $i" >&2 break 2 fi echo "Retry $retry for $i..." sleep 2 done done Logging in Loops
#!/bin/bash # Simple logging log_file="/tmp/script.log" exec > >(tee -a "$log_file") 2>&1 echo "Starting processing at $(date)" for i in {1..10}; do echo "Processing iteration $i" # Do work if [ $i -eq 5 ]; then echo "Warning: Something unusual at iteration 5" fi done echo "Finished at $(date)" # Detailed logging with levels log() { local level="$1" local message="$2" echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" } for i in {1..10}; do log "INFO" "Starting iteration $i" if ! some_command; then log "ERROR" "Command failed at iteration $i" continue fi log "INFO" "Completed iteration $i" done 12. Loop Performance Comparison
Performance Testing
#!/bin/bash # Test different loop types test_performance() { local iterations=10000 echo "Testing $iterations iterations:" echo "================================" # C-style for loop echo -n "C-style for: " time { for ((i=0; i<iterations; i++)); do : # null operation done } # Brace expansion for echo -n "Brace expansion for: " time { for i in {1..10000}; do : done } # While loop echo -n "While loop: " time { i=0 while [ $i -lt $iterations ]; do ((i++)) done } # Until loop echo -n "Until loop: " time { i=0 until [ $i -ge $iterations ]; do ((i++)) done } } # Test with arrays array_size=1000 echo "Testing with array of $array_size elements:" echo "==========================================" # Create test array test_array=($(seq 1 $array_size)) # For loop with elements echo -n "For loop with elements: " time { for elem in "${test_array[@]}"; do : done } # For loop with indices echo -n "For loop with indices: " time { for ((i=0; i<${#test_array[@]}; i++)); do : done } # While loop with counter echo -n "While loop with counter: " time { i=0 while [ $i -lt ${#test_array[@]} ]; do ((i++)) done } # Test file reading test_file="/tmp/test_file.txt" seq 1 1000 > "$test_file" echo "Testing file reading methods:" echo "============================" # While read echo -n "While read: " time { while read -r line; do : done < "$test_file" } # For loop with cat echo -n "For loop with cat: " time { IFS=$'\n' for line in $(cat "$test_file"); do : done } # mapfile echo -n "Mapfile: " time { mapfile -t lines < "$test_file" for line in "${lines[@]}"; do : done } rm "$test_file" 13. Loop Design Patterns
Common Design Patterns
#!/bin/bash # Producer-Consumer pattern producer() { for i in {1..10}; do echo "Producing item $i" echo "$i" > /tmp/item sleep 1 done } consumer() { while true; do if [ -f /tmp/item ]; then item=$(cat /tmp/item) rm /tmp/item echo "Consuming item $item" fi sleep 1 done } # Pipeline pattern pipeline() { generate_data() { for i in {1..100}; do echo $((RANDOM % 100)) done } filter() { while read -r num; do if [ $((num % 2)) -eq 0 ]; then echo "$num" fi done } transform() { while read -r num; do echo $((num * 2)) done } aggregate() { local sum=0 local count=0 while read -r num; do sum=$((sum + num)) ((count++)) done echo "Average: $((sum / count))" } generate_data | filter | transform | aggregate } # Batch processing pattern batch_process() { local batch_size=10 local batch=() for item in {1..100}; do batch+=("$item") if [ ${#batch[@]} -ge $batch_size ]; then echo "Processing batch: ${batch[*]}" # Process batch batch=() fi done # Process remaining if [ ${#batch[@]} -gt 0 ]; then echo "Processing final batch: ${batch[*]}" fi } # Rate limiting pattern rate_limit() { local delay=1 local max_per_second=5 local count=0 for i in {1..50}; do # Process item echo "Processing item $i" ((count++)) if [ $count -ge $max_per_second ]; then sleep $delay count=0 fi done } 14. Best Practices and Tips
Loop Best Practices
#!/bin/bash # 1. Always quote variables in loop conditions # Bad for file in $files; do echo "$file" done # Good for file in "${files[@]}"; do echo "$file" done # 2. Use while read for file processing # Bad for line in $(cat file.txt); do echo "$line" done # Good while IFS= read -r line; do echo "$line" done < file.txt # 3. Use proper IFS handling # Bad data="a:b:c" for item in $data; do echo "$item" done # Good IFS=':' read -ra items <<< "$data" for item in "${items[@]}"; do echo "$item" done # 4. Avoid unnecessary command substitution # Bad for i in $(seq 1 10); do echo "$i" done # Good for i in {1..10}; do echo "$i" done # 5. Use break and continue appropriately for file in *; do [ -f "$file" ] || continue # Skip non-files [ "$file" = "stop.txt" ] && break # Stop if special file process_file "$file" done # 6. Limit loop scope with subshells ( cd /some/dir || exit for file in *; do echo "In subdir: $file" done ) # Returns to original directory # 7. Use arrays for complex data # Bad fruits="apple banana orange" for fruit in $fruits; do echo "$fruit" done # Good fruits=("apple" "banana" "orange") for fruit in "${fruits[@]}"; do echo "$fruit" done # 8. Handle signals properly trap 'echo "Interrupted"; exit 1' INT for i in {1..10}; do echo "Working... ($i)" sleep 1 done # 9. Use progress indicators for long loops total=100 for ((i=1; i<=total; i++)); do # Do work printf "Progress: %d%%\r" $((i * 100 / total)) done echo # 10. Document complex loops # Process files in chunks to avoid memory issues # Chunk size: 100 files per batch chunk_size=100 files=("$@") for ((i=0; i<${#files[@]}; i+=chunk_size)); do end=$((i + chunk_size)) [ $end -gt ${#files[@]} ] && end=${#files[@]} echo "Processing files ${i} to $((end-1))" for ((j=i; j<end; j++)); do process "${files[$j]}" done done Loop Tips and Tricks
#!/bin/bash # 1. Loop over lines with line numbers line_num=1 while IFS= read -r line; do printf "%4d: %s\n" $line_num "$line" ((line_num++)) done < file.txt # 2. Loop with command grouping for i in {1..3}; do { echo "Starting iteration $i" some_long_running_task echo "Finished iteration $i" } & done wait # 3. Loop with timeout timeout=10 while [ $timeout -gt 0 ] && ! ping -c1 host; do echo "Waiting for host... ${timeout}s remaining" ((timeout--)) sleep 1 done # 4. Loop with conditional break found=false for file in *; do if grep -q "pattern" "$file"; then found=true break fi done $found && echo "Pattern found" || echo "Pattern not found" # 5. Loop with error accumulation errors=() for file in *; do if ! process "$file"; then errors+=("$file") fi done if [ ${#errors[@]} -gt 0 ]; then echo "Errors in: ${errors[*]}" fi # 6. Loop with parallel processing max_jobs=4 current_jobs=0 for file in *.txt; do process_file "$file" & ((current_jobs++)) if [ $current_jobs -ge $max_jobs ]; then wait current_jobs=0 fi done wait # 7. Loop with dynamic variable names for i in {1..5}; do declare "var_$i=Value$i" done for i in {1..5}; do var="var_$i" echo "${!var}" done # 8. Loop with associative arrays declare -A config config=( [host]="localhost" [port]="8080" [user]="admin" ) for key in "${!config[@]}"; do echo "$key = ${config[$key]}" done # 9. Loop with heredoc while read -r line; do echo "Line: $line" done << EOF This is line 1 This is line 2 This is line 3 EOF # 10. Loop with process substitution while read -r file; do echo "Found: $file" done < <(find . -name "*.txt") 15. Command Summary and Cheat Sheet
Loop Quick Reference
# For loops for var in list; do commands; done for ((init; condition; increment)); do commands; done # While loops while condition; do commands; done # Until loops until condition; do commands; done # Select loops select var in list; do commands; done # Loop control break [n] # Break out of loop (n levels) continue [n] # Skip to next iteration (n levels) exit [n] # Exit script with status n return [n] # Return from function with status n # Common patterns for i in {1..10}; do echo $i; done for ((i=1; i<=10; i++)); do echo $i; done while IFS= read -r line; do echo "$line"; done < file until ping -c1 host; do sleep 1; done select opt in choices; do case $opt in ...) break;; esac; done # Special variables $@ # All arguments $* # All arguments as string $# # Number of arguments $? # Exit status $$ # Process ID $! # Last background PID Loop Examples Summary
| Loop Type | Use Case | Example |
|---|---|---|
for in | Iterate over list | for i in 1 2 3; do echo $i; done |
for (( )) | Numeric iteration | for ((i=0; i<10; i++)); do echo $i; done |
while | Condition-based | while [ $count -lt 10 ]; do ((count++)); done |
until | Wait for condition | until ping -c1 host; do sleep 1; done |
select | Menu creation | select opt in Start Stop; do echo $opt; done |
Common Loop Operations
# Iterate over files for file in *.txt; do echo "$file" done # Iterate over array for elem in "${array[@]}"; do echo "$elem" done # Iterate over array indices for i in "${!array[@]}"; do echo "$i: ${array[$i]}" done # Read file line by line while IFS= read -r line; do echo "$line" done < file.txt # Process command output while IFS= read -r line; do echo "$line" done < <(command) # Infinite loop while true; do # Do something sleep 1 done # Loop with counter for ((i=1; i<=10; i++)); do echo "Iteration $i" done # Loop with step for i in {1..10..2}; do echo "Step: $i" done Conclusion
Bash loops are powerful constructs for automation and repetitive tasks:
Key Takeaways
- Four loop types:
for,while,until,selectfor different scenarios - Loop control:
break,continue,exitfor flow management - Performance: Choose the right loop type for your use case
- File processing:
while readis best for line-by-line processing - Arrays: Use arrays with loops for complex data structures
- Error handling: Always validate and handle errors in loops
Best Practices Summary
| Practice | Why |
|---|---|
| Quote variables | Prevent word splitting |
Use while read for files | Preserve whitespace |
| Prefer built-in operations | Better performance |
| Limit loop scope | Avoid side effects |
| Use arrays | Handle complex data |
| Add progress indicators | User feedback for long loops |
| Handle signals | Graceful interruption |
| Document complex loops | Maintainability |
Use break and continue | Efficient loop control |
| Test edge cases | Reliability |
Common Pitfalls
| Pitfall | Solution |
|---|---|
| Word splitting | Quote variables, set IFS |
| File globbing | Use arrays, quote variables |
| Subshell issues | Use process substitution |
| Performance | Use appropriate loop type |
| Memory | Process in chunks |
| Error handling | Check exit status, use set -e |
Mastering Bash loops is essential for effective shell scripting. They provide the foundation for automating repetitive tasks, processing data, and controlling program flow in the Unix/Linux environment.
Complete Bash Exercises with Solutions
Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/
Complete Guide to Bash Crontab Command
Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/
Complete Guide to Bash Arrays
Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/
Bash Quiz: Test Your Knowledge
Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/
Complete Guide to Bash Functions
Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/
Complete Guide to Bash Loops
Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/
Complete Guide to Bash If-Else Statements
Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/
Complete Guide to Bash Operators
Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/
Complete Guide to Bash Data Types
Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/
Complete Guide to Bash Variables
Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/
Complete Guide to Bash Scripting
A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/
Complete Guide to Basic Bash Syntax
Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/
Complete Guide to Bash Chgrp Command
Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/
Complete Guide to Bash Chgrp Command (Alternate)
Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/
Complete Guide to Bash File Permissions and Ownership
Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/
Complete Guide to Bash Chmod Command
Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/
Complete Guide to Bash Chown Command
Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/
Complete Guide to Bash SCP Command
Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/
Complete Guide to Bash SSH Command
Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/
Bash Wget Command Complete Guide
Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/
