Shell Script Loops: Complete Guide and Reference

Master shell script loops with this comprehensive guide covering for, while, and until loops, control statements, nested loops, and best practices.

Shell Script Loops: Complete Guide and Reference

Table of Contents

1. [Introduction to Shell Loops](#introduction) 2. [Types of Loops](#types-of-loops) 3. [For Loops](#for-loops) 4. [While Loops](#while-loops) 5. [Until Loops](#until-loops) 6. [Loop Control Statements](#loop-control-statements) 7. [Nested Loops](#nested-loops) 8. [Practical Examples](#practical-examples) 9. [Best Practices](#best-practices) 10. [Common Pitfalls](#common-pitfalls)

Introduction to Shell Loops {#introduction}

Shell loops are fundamental control structures that allow you to execute a block of code repeatedly based on specific conditions or iterate through collections of data. They are essential for automating repetitive tasks, processing files, handling arrays, and creating efficient shell scripts.

Loops in shell scripting provide the ability to: - Process multiple files or directories - Repeat operations until a condition is met - Iterate through command-line arguments - Handle data from files or command output - Create menu-driven interfaces - Perform batch operations

Types of Loops {#types-of-loops}

Shell scripting supports three primary types of loops, each designed for specific use cases:

| Loop Type | Purpose | When to Use | |-----------|---------|-------------| | for | Iterate through a known list or range | When you know the number of iterations or have a specific set of items | | while | Continue while condition is true | When you need to loop based on a condition that may change during execution | | until | Continue until condition becomes true | When you want to loop until a specific condition is achieved |

Loop Syntax Comparison Table

| Loop Type | Basic Syntax | Condition Check | Termination | |-----------|--------------|-----------------|-------------| | for | for var in list; do ... done | Implicit (list exhaustion) | When list is exhausted | | while | while condition; do ... done | At beginning of each iteration | When condition becomes false | | until | until condition; do ... done | At beginning of each iteration | When condition becomes true |

For Loops {#for-loops}

The for loop is the most commonly used loop in shell scripting. It iterates through a list of items, executing the loop body for each item.

Basic For Loop Syntax

`bash for variable in list do # commands to execute done `

Alternative Compact Syntax

`bash for variable in list; do commands; done `

For Loop Variations

#### 1. Iterating Through a Simple List

`bash #!/bin/bash

Example 1: Basic list iteration

for fruit in apple banana orange grape do echo "Processing: $fruit" done `

Output: ` Processing: apple Processing: banana Processing: orange Processing: grape `

#### 2. Iterating Through Files

`bash #!/bin/bash

Example 2: Processing files in current directory

for file in *.txt do if [ -f "$file" ]; then echo "Found text file: $file" wc -l "$file" fi done `

#### 3. C-Style For Loop

`bash #!/bin/bash

Example 3: C-style for loop with numeric range

for ((i=1; i<=10; i++)) do echo "Number: $i" done `

#### 4. Using Brace Expansion

`bash #!/bin/bash

Example 4: Brace expansion for ranges

Numeric range

for num in {1..5} do echo "Count: $num" done

Character range

for letter in {a..e} do echo "Letter: $letter" done

Step increment

for num in {0..20..5} do echo "Step by 5: $num" done `

#### 5. Command Substitution in For Loops

`bash #!/bin/bash

Example 5: Using command output as list

Process all users from /etc/passwd

for user in $(cut -d: -f1 /etc/passwd | head -5) do echo "User: $user" done

Process files from find command

for file in $(find /tmp -name "*.log" -type f) do echo "Log file: $file" tail -5 "$file" done `

#### 6. Array Iteration

`bash #!/bin/bash

Example 6: Iterating through arrays

Declare array

colors=("red" "green" "blue" "yellow")

Method 1: Direct array expansion

for color in "${colors[@]}" do echo "Color: $color" done

Method 2: Index-based iteration

for i in "${!colors[@]}" do echo "Index $i: ${colors[$i]}" done `

For Loop with Command Line Arguments

`bash #!/bin/bash

Example 7: Processing command line arguments

echo "Processing all arguments:" for arg in "$@" do echo "Argument: $arg" done

Alternative using $*

echo "Using \$*:" for arg in $* do echo "Argument: $arg" done `

While Loops {#while-loops}

The while loop continues executing as long as the specified condition remains true. It's ideal for situations where you don't know the exact number of iterations needed.

Basic While Loop Syntax

`bash while condition do # commands to execute done `

While Loop Examples

#### 1. Basic Counter Loop

`bash #!/bin/bash

Example 1: Simple counter

counter=1 while [ $counter -le 5 ] do echo "Count: $counter" counter=$((counter + 1)) done `

#### 2. Reading File Line by Line

`bash #!/bin/bash

Example 2: Reading file contents

filename="data.txt" line_number=1

while IFS= read -r line do echo "Line $line_number: $line" line_number=$((line_number + 1)) done < "$filename" `

#### 3. Menu-Driven Interface

`bash #!/bin/bash

Example 3: Interactive menu

choice="" while [ "$choice" != "quit" ] do echo "Menu Options:" echo "1. List files" echo "2. Show date" echo "3. Show users" echo "Type 'quit' to exit" read -p "Enter your choice: " choice case $choice in 1) ls -la ;; 2) date ;; 3) who ;; quit) echo "Goodbye!" ;; *) echo "Invalid option" ;; esac done `

#### 4. Monitoring System Resources

`bash #!/bin/bash

Example 4: System monitoring

while true do clear echo "System Monitor - $(date)" echo "========================" # CPU usage echo "CPU Usage:" top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 # Memory usage echo "Memory Usage:" free -h | grep Mem # Disk usage echo "Disk Usage:" df -h | grep -E '^/dev/' echo "Press Ctrl+C to exit" sleep 5 done `

#### 5. Waiting for File Creation

`bash #!/bin/bash

Example 5: Wait for file to be created

target_file="/tmp/important_file.txt"

echo "Waiting for $target_file to be created..." while [ ! -f "$target_file" ] do echo "Still waiting... $(date)" sleep 2 done

echo "File $target_file has been created!" `

#### 6. Reading User Input Until Valid

`bash #!/bin/bash

Example 6: Input validation

valid_input=false while [ "$valid_input" = false ] do read -p "Enter a number between 1 and 10: " number if [[ "$number" =~ ^[0-9]+$ ]] && [ "$number" -ge 1 ] && [ "$number" -le 10 ]; then echo "Valid input: $number" valid_input=true else echo "Invalid input. Please try again." fi done `

Until Loops {#until-loops}

The until loop is the opposite of the while loop. It continues executing until the specified condition becomes true.

Basic Until Loop Syntax

`bash until condition do # commands to execute done `

Until Loop Examples

#### 1. Basic Counter with Until

`bash #!/bin/bash

Example 1: Counter using until

counter=1 until [ $counter -gt 5 ] do echo "Count: $counter" counter=$((counter + 1)) done `

#### 2. Wait Until Service is Running

`bash #!/bin/bash

Example 2: Wait for service to start

service_name="apache2"

echo "Waiting for $service_name to start..." until systemctl is-active --quiet $service_name do echo "Service not running, waiting..." sleep 3 done

echo "$service_name is now running!" `

#### 3. Wait Until File Size Reaches Threshold

`bash #!/bin/bash

Example 3: Monitor file size

target_file="/var/log/application.log" target_size=1048576 # 1MB in bytes

until [ -f "$target_file" ] && [ $(stat -f%z "$target_file" 2>/dev/null || stat -c%s "$target_file" 2>/dev/null || echo 0) -ge $target_size ] do current_size=$(stat -f%z "$target_file" 2>/dev/null || stat -c%s "$target_file" 2>/dev/null || echo 0) echo "Current file size: $current_size bytes, waiting for $target_size bytes..." sleep 5 done

echo "File has reached the target size!" `

#### 4. Network Connectivity Check

`bash #!/bin/bash

Example 4: Wait for network connectivity

host="google.com"

echo "Checking network connectivity to $host..." until ping -c 1 "$host" &> /dev/null do echo "No connectivity to $host, retrying..." sleep 5 done

echo "Network connectivity to $host established!" `

Loop Control Statements {#loop-control-statements}

Loop control statements allow you to alter the normal flow of loop execution.

Control Statement Types

| Statement | Purpose | Effect | |-----------|---------|---------| | break | Exit the loop immediately | Terminates the current loop and continues with the next statement after the loop | | continue | Skip current iteration | Skips the remaining commands in the current iteration and moves to the next iteration |

Break Statement Examples

#### 1. Breaking Out of For Loop

`bash #!/bin/bash

Example 1: Break in for loop

for num in {1..10} do if [ $num -eq 6 ]; then echo "Breaking at $num" break fi echo "Number: $num" done echo "Loop ended" `

#### 2. Breaking Out of While Loop

`bash #!/bin/bash

Example 2: Break in while loop

counter=1 while true do echo "Counter: $counter" if [ $counter -eq 5 ]; then echo "Breaking out of infinite loop" break fi counter=$((counter + 1)) done `

#### 3. Breaking Out of Nested Loops

`bash #!/bin/bash

Example 3: Breaking from nested loops

for i in {1..3} do echo "Outer loop: $i" for j in {1..5} do if [ $j -eq 3 ]; then echo " Breaking inner loop at j=$j" break fi echo " Inner loop: $j" done done `

Continue Statement Examples

#### 1. Skipping Even Numbers

`bash #!/bin/bash

Example 1: Skip even numbers

for num in {1..10} do if [ $((num % 2)) -eq 0 ]; then continue fi echo "Odd number: $num" done `

#### 2. Processing Valid Files Only

`bash #!/bin/bash

Example 2: Process only readable files

for file in /etc/* do if [ ! -r "$file" ]; then echo "Skipping unreadable file: $file" continue fi if [ -d "$file" ]; then echo "Skipping directory: $file" continue fi echo "Processing file: $file" # Process the file here done `

Nested Loops {#nested-loops}

Nested loops are loops inside other loops. They're useful for processing multi-dimensional data or performing complex iterations.

Nested Loop Examples

#### 1. Multiplication Table

`bash #!/bin/bash

Example 1: Multiplication table

echo "Multiplication Table (1-5):" echo " | 1 2 3 4 5" echo "---|---------------"

for i in {1..5} do printf "%2d | " $i for j in {1..5} do result=$((i * j)) printf "%2d " $result done echo done `

#### 2. File Processing in Multiple Directories

`bash #!/bin/bash

Example 2: Process files in multiple directories

directories=("/home/user/docs" "/home/user/projects" "/home/user/scripts") extensions=("txt" "log" "conf")

for dir in "${directories[@]}" do if [ -d "$dir" ]; then echo "Processing directory: $dir" for ext in "${extensions[@]}" do echo " Looking for .$ext files:" for file in "$dir"/*.$ext do if [ -f "$file" ]; then echo " Found: $(basename "$file")" fi done done fi done `

#### 3. Matrix Operations

`bash #!/bin/bash

Example 3: Simple matrix display

rows=3 cols=4

echo "Matrix ${rows}x${cols}:" for ((i=1; i<=rows; i++)) do for ((j=1; j<=cols; j++)) do value=$((i * cols + j - cols)) printf "%3d " $value done echo done `

Practical Examples {#practical-examples}

Example 1: Log File Analyzer

`bash #!/bin/bash

Log file analyzer script

log_file="/var/log/access.log" temp_dir="/tmp/log_analysis"

Create temporary directory

mkdir -p "$temp_dir"

echo "Analyzing log file: $log_file" echo "================================"

Count different HTTP status codes

declare -A status_codes while IFS= read -r line do # Extract status code (assuming Apache common log format) status=$(echo "$line" | awk '{print $9}') if [[ "$status" =~ ^[0-9]{3}$ ]]; then ((status_codes[$status]++)) fi done < "$log_file"

echo "HTTP Status Code Summary:" for status in "${!status_codes[@]}" do echo "Status $status: ${status_codes[$status]} occurrences" done

Find top 10 IP addresses

echo -e "\nTop 10 IP Addresses:" awk '{print $1}' "$log_file" | sort | uniq -c | sort -nr | head -10 | while read count ip do echo "IP: $ip - $count requests" done `

Example 2: System Backup Script

`bash #!/bin/bash

System backup script with progress indication

backup_source="/home/user/important_data" backup_dest="/backup/$(date +%Y%m%d_%H%M%S)" exclude_patterns=(".tmp" ".log" "cache/*")

echo "Starting backup process..." echo "Source: $backup_source" echo "Destination: $backup_dest"

Create backup directory

mkdir -p "$backup_dest"

Build exclude options

exclude_opts="" for pattern in "${exclude_patterns[@]}" do exclude_opts="$exclude_opts --exclude=$pattern" done

Get total number of files to backup

total_files=$(find "$backup_source" -type f | wc -l) current_file=0

echo "Total files to backup: $total_files" echo "Progress:"

Backup with progress indication

find "$backup_source" -type f | while read -r file do ((current_file++)) # Calculate progress percentage progress=$((current_file * 100 / total_files)) # Copy file maintaining directory structure relative_path="${file#$backup_source/}" dest_file="$backup_dest/$relative_path" dest_dir=$(dirname "$dest_file") mkdir -p "$dest_dir" cp "$file" "$dest_file" # Display progress every 10 files if [ $((current_file % 10)) -eq 0 ] || [ $current_file -eq $total_files ]; then printf "\rProgress: [%-50s] %d%% (%d/%d files)" \ "$(printf '#%.0s' $(seq 1 $((progress/2))))" \ "$progress" "$current_file" "$total_files" fi done

echo -e "\nBackup completed successfully!" echo "Backup location: $backup_dest" `

Example 3: Network Port Scanner

`bash #!/bin/bash

Simple network port scanner

target_host="$1" start_port="${2:-1}" end_port="${3:-1000}" timeout=1

if [ -z "$target_host" ]; then echo "Usage: $0 [start_port] [end_port]" echo "Example: $0 192.168.1.1 1 1000" exit 1 fi

echo "Scanning $target_host for open ports ($start_port-$end_port)..." echo "================================================"

open_ports=() closed_count=0

for ((port=start_port; port<=end_port; port++)) do # Use timeout and netcat to check port if timeout $timeout bash -c "echo >/dev/tcp/$target_host/$port" 2>/dev/null; then echo "Port $port: OPEN" open_ports+=($port) else ((closed_count++)) fi # Progress indicator if [ $((port % 100)) -eq 0 ]; then echo "Scanned up to port $port..." fi done

echo "================================================" echo "Scan completed!" echo "Open ports: ${open_ports[@]}" echo "Closed/filtered ports: $closed_count" `

Best Practices {#best-practices}

Performance Optimization

| Practice | Description | Example | |----------|-------------|---------| | Use appropriate loop type | Choose the right loop for your use case | Use for for known lists, while for conditions | | Minimize command substitution | Avoid expensive operations inside loops | Store command output in variables before the loop | | Use built-in operations | Prefer shell built-ins over external commands | Use $((i++)) instead of i=$(expr $i + 1) | | Quote variables properly | Always quote variables to handle spaces | "$variable" instead of $variable |

Code Quality Guidelines

#### 1. Proper Variable Quoting

`bash

Good practice

for file in "$@" do if [ -f "$file" ]; then echo "Processing: $file" fi done

Bad practice - unquoted variables

for file in $@ do if [ -f $file ]; then echo "Processing: $file" fi done `

#### 2. Error Handling in Loops

`bash #!/bin/bash

Good practice with error handling

files_processed=0 errors=0

for file in *.txt do if [ ! -f "$file" ]; then echo "No .txt files found" break fi if cp "$file" "/backup/" 2>/dev/null; then echo "Successfully copied: $file" ((files_processed++)) else echo "Error copying: $file" >&2 ((errors++)) fi done

echo "Summary: $files_processed files processed, $errors errors" `

#### 3. Using Arrays Effectively

`bash #!/bin/bash

Efficient array processing

Declare array

declare -a servers=("web1.example.com" "web2.example.com" "db1.example.com")

Process array efficiently

for server in "${servers[@]}" do echo "Checking server: $server" if ping -c 1 "$server" &>/dev/null; then echo " $server is reachable" else echo " $server is unreachable" fi done `

Memory and Resource Management

#### 1. Handling Large Files

`bash #!/bin/bash

Memory-efficient file processing

large_file="/var/log/huge_logfile.log" chunk_size=1000

line_count=0 while IFS= read -r line do # Process line echo "Processing line $((++line_count)): ${line:0:50}..." # Process in chunks to avoid memory issues if [ $((line_count % chunk_size)) -eq 0 ]; then echo "Processed $line_count lines, pausing..." sleep 1 fi done < "$large_file" `

#### 2. Resource Cleanup

`bash #!/bin/bash

Proper resource cleanup

temp_files=() trap 'cleanup' EXIT

cleanup() { echo "Cleaning up temporary files..." for temp_file in "${temp_files[@]}" do if [ -f "$temp_file" ]; then rm "$temp_file" echo "Removed: $temp_file" fi done }

Main processing loop

for i in {1..5} do temp_file="/tmp/process_$i.tmp" temp_files+=("$temp_file") echo "Creating temporary file: $temp_file" echo "Data for process $i" > "$temp_file" # Process the temporary file cat "$temp_file" done `

Common Pitfalls and Solutions {#common-pitfalls}

Pitfall Analysis Table

| Pitfall | Problem | Solution | Example | |---------|---------|----------|---------| | Infinite loops | Loop condition never becomes false | Always ensure loop variable is modified | Use counters or break conditions | | Unquoted variables | Variables with spaces break | Always quote variables | Use "$var" not $var | | Command substitution in loops | Performance degradation | Move expensive operations outside | Pre-calculate values | | File globbing issues | Patterns don't match files | Check for file existence | Use conditional checks |

Common Mistakes and Fixes

#### 1. Infinite Loop Prevention

`bash

Problem: Infinite loop

counter=1 while [ $counter -le 10 ] do echo "Count: $counter" # Missing: counter increment done

Solution: Always modify loop variable

counter=1 while [ $counter -le 10 ] do echo "Count: $counter" counter=$((counter + 1)) # Essential increment done `

#### 2. Proper File Handling

`bash

Problem: Doesn't handle files with spaces

for file in $(ls *.txt) do echo "Processing: $file" done

Solution: Use glob patterns directly

for file in *.txt do if [ -f "$file" ]; then # Check file exists echo "Processing: $file" fi done `

#### 3. Array Iteration Issues

`bash

Problem: Incorrect array iteration

array=("item one" "item two" "item three") for item in ${array[@]} # Unquoted - splits on spaces do echo "Item: $item" done

Solution: Proper quoting

array=("item one" "item two" "item three") for item in "${array[@]}" # Quoted - preserves spaces do echo "Item: $item" done `

#### 4. Loop Variable Scope

`bash

Problem: Variable scope in subshells

total=0 echo "1 2 3 4 5" | while read -r num do total=$((total + num)) # This runs in a subshell done echo "Total: $total" # Still 0 - subshell variable not accessible

Solution: Use process substitution or here-string

total=0 while read -r num do total=$((total + num)) done <<< "1 2 3 4 5" echo "Total: $total" # Correct total `

This comprehensive guide covers all aspects of using loops in shell scripts, from basic syntax to advanced techniques and best practices. The examples provided demonstrate real-world applications and common scenarios you'll encounter when writing shell scripts with loops.

Tags

  • Automation
  • Linux
  • bash
  • loops
  • shell-scripting

Related Articles

Popular Technical Articles & Tutorials

Explore our comprehensive collection of technical articles, programming tutorials, and IT guides written by industry experts:

Browse all 8+ technical articles | Read our IT blog

Shell Script Loops: Complete Guide and Reference