Skip to content

Loop_control

Chapter 10: Loop Control (break, continue)

Section titled “Chapter 10: Loop Control (break, continue)”

This chapter covers the control statements that modify loop behavior: break and continue. These statements provide fine-grained control over loop execution, enabling complex logic patterns commonly needed in DevOps scripts for error handling, search operations, and state machine implementations.


The break statement immediately exits the innermost loop.

break_basic.sh
#!/usr/bin/env bash
# Find first matching file
for file in /tmp/*.txt; do
echo "Checking: $file"
if [ -s "$file" ]; then
echo "Found non-empty file: $file"
break # Exit loop
fi
done

You can specify how many loop levels to break out of.

break_levels.sh
#!/usr/bin/env bash
# Break out of multiple loops
for i in {1..3}; do
echo "Outer loop: $i"
for j in {1..3}; do
echo " Inner loop: $j"
if [ $j -eq 2 ]; then
break 2 # Break out of both loops
fi
done
done

The continue statement skips the rest of the current iteration and moves to the next iteration.

continue_basic.sh
#!/usr/bin/env bash
# Skip even numbers
for i in {1..10}; do
if [ $((i % 2)) -eq 0 ]; then
continue # Skip this iteration
fi
echo "Odd number: $i"
done

You can continue to a specific loop level using labels.

continue_label.sh
#!/usr/bin/env bash
# Continue to outer loop
for i in {1..3}; do
for j in {1..3}; do
if [ $j -eq 2 ]; then
continue # Continue inner loop
fi
echo "i=$i, j=$j"
done
done
echo "---"
# Using label
outer_loop:
for i in {1..3}; do
for j in {1..3}; do
if [ $j -eq 2 ]; then
continue outer_loop # Continue outer loop
fi
echo "i=$i, j=$j"
done
done

find_first.sh
#!/usr/bin/env bash
# Pattern: Find first matching item
services=("nginx" "mysql" "redis" "postgresql")
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service"; then
echo "Found active service: $service"
break
fi
done
process_until.sh
#!/usr/bin/env bash
# Pattern: Process until condition is met
count=0
max_iterations=10
while true; do
count=$((count + 1))
echo "Processing iteration: $count"
# Simulate work
sleep 1
if [ $count -ge $max_iterations ]; then
echo "Maximum iterations reached"
break
fi
done
skip_invalid.sh
#!/usr/bin/env bash
# Pattern: Skip invalid items in processing
files=(
"/etc/passwd"
"/nonexistent/file1.txt"
"/etc/hosts"
"/nonexistent/file2.txt"
"/etc/fstab"
)
for file in "${files[@]}"; do
# Skip if file doesn't exist
if [ ! -f "$file" ]; then
echo "Skipping (not found): $file"
continue
fi
# Process valid file
lines=$(wc -l < "$file")
echo "Processed: $file ($lines lines)"
done
count_results.sh
#!/usr/bin/env bash
# Pattern: Count successes and failures
servers=("server1" "server2" "server3" "server4")
success=0
failure=0
for server in "${servers[@]}"; do
if ping -c 1 -W 2 "$server" &>/dev/null; then
echo "$server is reachable"
success=$((success + 1))
else
echo "$server is not reachable"
failure=$((failure + 1))
fi
done
echo "---"
echo "Success: $success"
echo "Failure: $failure"

search_files.sh
#!/usr/bin/env bash
set -euo pipefail
# Search for pattern in files, stop after first match
search_pattern="$1"
search_dir="${2:-.}"
if [ -z "$search_pattern" ]; then
echo "Usage: $0 <pattern> [directory]"
exit 1
fi
echo "Searching for '$search_pattern' in $search_dir..."
for file in $(find "$search_dir" -type f -name "*.log" 2>/dev/null); do
if grep -q "$search_pattern" "$file"; then
echo "Found in: $file"
echo "Content:"
grep "$search_pattern" "$file"
break # Stop after first match
fi
done
retry_timeout.sh
#!/usr/bin/env bash
set -euo pipefail
# Function to simulate a command that might fail
attempt_command() {
local attempt=$1
echo "Attempt $attempt..."
# Simulate occasional failure
if [ $((attempt % 3)) -ne 0 ]; then
return 1
fi
return 0
}
# Retry loop with timeout
max_attempts=10
attempt=0
while [ $attempt -lt $max_attempts ]; do
attempt=$((attempt + 1))
if attempt_command $attempt; then
echo "Success on attempt $attempt"
exit 0
fi
echo "Attempt $attempt failed, retrying in 2 seconds..."
sleep 2
done
echo "Failed after $max_attempts attempts"
exit 1
error_limit.sh
#!/usr/bin/env bash
set -euo pipefail
# Process items, stop after too many errors
items=(item1 item2 item3 item4 item5)
max_errors=2
errors=0
processed=0
for item in "${items[@]}"; do
# Check error limit
if [ $errors -ge $max_errors ]; then
echo "Error limit reached ($max_errors), stopping"
break
fi
echo "Processing: $item"
# Simulate processing (randomly succeed/fail)
if [ $((RANDOM % 2)) -eq 0 ]; then
echo " ✓ Success"
processed=$((processed + 1))
else
echo " ✗ Failed"
errors=$((errors + 1))
fi
done
echo "---"
echo "Processed: $processed"
echo "Errors: $errors"
menu_exit.sh
#!/usr/bin/env bash
# Interactive menu with exit option
while true; do
echo "=== Menu ==="
echo "1. Option A"
echo "2. Option B"
echo "3. Option C"
echo "4. Exit"
echo "============"
read -p "Choice: " choice
case "$choice" in
1)
echo "Selected Option A"
;;
2)
echo "Selected Option B"
;;
3)
echo "Selected Option C"
;;
4)
echo "Exiting..."
break # Exit the loop
;;
*)
echo "Invalid choice"
;;
esac
echo ""
done

Example 5: Process Logs Until Critical Error

Section titled “Example 5: Process Logs Until Critical Error”
process_until_error.sh
#!/usr/bin/env bash
set -euo pipefail
# Read log lines, process until critical error
log_file="${1:-/var/log/syslog}"
if [ ! -f "$log_file" ]; then
echo "Log file not found: $log_file"
exit 1
fi
error_count=0
critical_found=false
while IFS= read -r line; do
if echo "$line" | grep -q "CRITICAL"; then
echo "CRITICAL error found!"
echo "$line"
critical_found=true
break # Stop on critical error
elif echo "$line" | grep -q "ERROR"; then
error_count=$((error_count + 1))
echo "ERROR: $line"
else
# Process other lines
:
fi
done < "$log_file"
echo "---"
echo "Processed until first critical error"
echo "Errors found: $error_count"
skip_hidden.sh
#!/usr/bin/env bash
# Process directory, skip hidden files and directories
dir="${1:-.}"
for item in "$dir"/*; do
# Skip if doesn't exist (nullglob)
[ -e "$item" ] || continue
# Get basename
name=$(basename "$item")
# Skip hidden files
if [[ "$name" == .* ]]; then
continue
fi
# Process
if [ -d "$item" ]; then
echo "Directory: $name"
elif [ -f "$item" ]; then
echo "File: $name"
fi
done

nested_break.sh
#!/usr/bin/env bash
# Search in 3D grid
found=false
for x in {1..3}; do
for y in {1..3}; do
for z in {1..3}; do
echo "Checking ($x, $y, $z)"
# Simulate finding target at (2, 2, 2)
if [ $x -eq 2 ] && [ $y -eq 2 ] && [ $z -eq 2 ]; then
echo "Found at ($x, $y, $z)"
found=true
break 3 # Break out of all three loops
fi
done
done
done
if [ "$found" = true ]; then
echo "Search complete - found"
else
echo "Search complete - not found"
fi
continue_counter.sh
#!/usr/bin/env bash
# Process items, skip some based on counter
items=(a b c d e f g h i j)
skip_count=3
current_skip=0
for item in "${items[@]}";
do
# Skip every Nth item
current_skip=$((current_skip + 1))
if [ $current_skip -eq $skip_count ]; then
current_skip=0
echo "Skipping: $item"
continue
fi
echo "Processing: $item"
done
break_in_functions.sh
#!/usr/bin/env bash
# Using return to exit loops in functions
find_in_array() {
local needle="$1"
shift
local haystack=("$@")
for i in "${!haystack[@]}"; do
if [ "${haystack[$i]}" = "$needle" ]; then
return $i # Return index
fi
done
return 255 # Not found
}
# Test
array=("apple" "banana" "cherry" "date")
find_in_array "cherry" "${array[@]}"
result=$?
if [ $result -eq 255 ]; then
echo "Not found"
else
echo "Found at index: $result"
fi

mistake1.sh
#!/usr/bin/env bash
# WRONG: Infinite loop without break
counter=0
while true; do
echo "Counter: $counter"
counter=$((counter + 1))
# Missing break - infinite loop!
done
# CORRECT: Always have exit condition
counter=0
while true; do
echo "Counter: $counter"
counter=$((counter + 1))
if [ $counter -ge 10 ]; then
break
fi
done
mistake2.sh
#!/usr/bin/env bash
# WRONG: Continue inside nested structure
for i in {1..5}; do
echo "Outer: $i"
{
continue # This will error or behave unexpectedly
}
done
# CORRECT: Use proper structure
for i in {1..5}; do
if [ $i -eq 3 ]; then
continue # Skip iteration
fi
echo "Value: $i"
done

In this chapter, you learned about:

  • ✅ The break statement basics
  • ✅ Breaking out of multiple levels
  • ✅ The continue statement basics
  • ✅ Continuing with labels
  • ✅ Common loop control patterns
  • ✅ Find first match pattern
  • ✅ Process until condition pattern
  • ✅ Skip invalid items pattern
  • ✅ Count success/failure pattern
  • ✅ Practical DevOps examples
  • ✅ Advanced nested loop patterns
  • ✅ Common mistakes and corrections

  1. Write a script that finds the first file matching a pattern
  2. Create a script that skips even numbers in a loop
  3. Write a menu system using break
  1. Implement a retry mechanism with break
  2. Create a script that processes until error limit
  3. Write a log processor that stops on critical error
  1. Implement a search in multidimensional data
  2. Create a concurrent worker with error handling
  3. Build a state machine using loop control

Continue to the next chapter to learn about arrays in bash.


Previous Chapter: Loops Next Chapter: Arrays