Guide Bash-Scripting Beginner

Loops

for, while, and until loops in Bash — iterating over lists, files, command output, and ranges, plus break and continue.

3 min read

for Loops

# Iterate over a fixed list
for env in dev staging production; do
    echo "Deploying to $env"
done

# Iterate over a numeric range
for i in {1..5}; do
    echo "Attempt $i"
done

# C-style for loop
for ((i = 0; i < 5; i++)); do
    echo "Index: $i"
done

# Iterate over files matching a glob
for file in /var/log/*.log; do
    echo "Processing $file"
done

# Iterate over an array
SERVERS=("web1" "web2" "web3")
for server in "${SERVERS[@]}"; do
    echo "Checking $server"
done

while Loops

COUNT=0
while [ "$COUNT" -lt 5 ]; do
    echo "Count: $COUNT"
    ((COUNT++))
done

# Reading a file line by line — the standard, correct pattern
while IFS= read -r LINE; do
    echo "Line: $LINE"
done < servers.txt

# Infinite loop with a break condition — common for polling
while true; do
    if curl -sf http://localhost:8080/health > /dev/null; then
        echo "Service is healthy"
        break
    fi
    echo "Waiting for service..."
    sleep 2
done

until Loops (Inverse of while)

COUNT=0
until [ "$COUNT" -ge 5 ]; do
    echo "Count: $COUNT"
    ((COUNT++))
done

until runs while the condition is false — the exact inverse of while. Useful for “keep retrying until success” patterns.

until curl -sf http://localhost:8080/health > /dev/null; do
    echo "Service not ready, retrying..."
    sleep 2
done
echo "Service is up!"

break and continue

for i in {1..10}; do
    if [ "$i" -eq 5 ]; then
        break          # exit the loop entirely
    fi
    echo "$i"
done

for i in {1..10}; do
    if (( i % 2 == 0 )); then
        continue        # skip to the next iteration
    fi
    echo "$i"     # only odd numbers print
done

Looping Over Command Output

for pid in $(pgrep nginx); do
    echo "nginx process: $pid"
done

# Safer for filenames with spaces: use a while+read loop with process substitution
while IFS= read -r line; do
    echo "Found: $line"
done < <(find /var/log -name "*.log")

A Real-World Retry Loop

MAX_RETRIES=5
RETRY=0

until curl -sf https://api.example.com/health > /dev/null; do
    ((RETRY++))
    if [ "$RETRY" -ge "$MAX_RETRIES" ]; then
        echo "Failed after $MAX_RETRIES attempts" >&2
        exit 1
    fi
    echo "Retry $RETRY/$MAX_RETRIES..."
    sleep $((RETRY * 2))    # exponential-ish backoff
done
echo "Service is healthy"

Production Considerations

  • For iterating over command output that might contain filenames with spaces, prefer while read with process substitution (< <(command)) over for x in $(command), which word-splits on whitespace and breaks.
  • Always cap retry loops with a maximum attempt count — an unconditional while true retry loop with no exit condition can hang a script (and a pipeline) forever.
  • Add sleep between polling iterations to avoid hammering a service or API with a tight, CPU-spinning loop.

Quick Interview Answer

for iterates over a fixed list, range, glob, or array; while repeats as long as a condition is true, commonly used for reading files line-by-line or polling until a service is healthy; until is while’s inverse, useful for ‘retry until success’ patterns. break exits a loop entirely, continue skips to the next iteration. For command output that might contain spaces, while read with process substitution is safer than for x in $(cmd).”

Common Mistakes

  • Using for x in $(command) on output that might contain spaces or special characters, causing incorrect word-splitting.
  • Writing an unbounded retry loop with no maximum attempt count or timeout.
  • Forgetting IFS= and -r when reading files line-by-line, silently mangling whitespace and backslashes.

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form