2024-03-08
5 min read

How to Iterate Over a Range of Numbers in Bash Using Variables

How to Iterate Over a Range of Numbers in Bash Using Variables

Iterating over a range of numbers is a fundamental task in Bash scripting. When the range boundaries are stored in variables rather than hardcoded values, you need specific techniques to make the loop work correctly.

The Challenge with Variable Ranges

Unlike languages that support dynamic range syntax, Bash requires careful handling when loop boundaries come from variables. The standard brace expansion {1..10} doesn't work with variables because Bash expands braces before variable substitution occurs.

# This won't work as expected
start=5
end=15
for i in {$start..$end}; do
    echo $i
done
# Output: {5..15} (literal string, not expanded)

Method 1: Using seq Command

The seq command generates sequences of numbers and works seamlessly with variables:

start=5
end=15

for i in $(seq $start $end); do
    echo "Processing item $i"
done

You can also specify a step value for non-consecutive numbers:

start=2
end=20
step=3

for i in $(seq $start $step $end); do
    echo "Number: $i"
done
# Output: 2, 5, 8, 11, 14, 17, 20

The seq command is particularly useful for floating-point sequences:

for i in $(seq 1.5 0.5 3.5); do
    echo "Value: $i"
done
# Output: 1.5, 2.0, 2.5, 3.0, 3.5

Method 2: C-Style For Loop

Bash supports C-style for loops, which handle variables naturally:

start=10
end=20

for ((i=start; i<=end; i++)); do
    echo "Current number: $i"
done

This method gives you full control over the increment logic:

start=1
end=100

# Skip even numbers
for ((i=start; i<=end; i+=2)); do
    echo "Odd number: $i"
done

You can also decrement through ranges:

start=10
end=1

for ((i=start; i>=end; i--)); do
    echo "Countdown: $i"
done

Method 3: While Loop with Counter

For more complex iteration logic, use a while loop with a manual counter:

start=5
end=25
counter=$start

while [ $counter -le $end ]; do
    echo "Processing iteration $counter"

    # Your logic here
    if [ $((counter % 5)) -eq 0 ]; then
        echo "  Milestone reached at $counter"
    fi

    counter=$((counter + 1))
done

This approach works well when you need conditional logic within the loop or when dealing with complex stepping patterns.

Method 4: Using eval with Brace Expansion

You can force brace expansion to work with variables using eval, though this method requires caution:

start=3
end=8

for i in $(eval echo {$start..$end}); do
    echo "Number: $i"
done

Be careful with eval as it can execute arbitrary code. Only use this method when you control the input variables and understand the security implications.

Practical Examples

Here's a real-world example that processes log files based on date ranges:

#!/bin/bash

start_date=20240301
end_date=20240307
current_date=$start_date

while [ $current_date -le $end_date ]; do
    log_file="/var/log/app-$current_date.log"

    if [ -f "$log_file" ]; then
        echo "Processing $log_file"
        grep "ERROR" "$log_file" > "errors-$current_date.txt"
    else
        echo "Log file $log_file not found"
    fi

    # Increment date (simplified for consecutive dates)
    current_date=$((current_date + 1))
done

For batch file operations with numbered files:

start_file=1
end_file=50

for file_num in $(seq $start_file $end_file); do
    input_file="data_${file_num}.txt"
    output_file="processed_${file_num}.txt"

    if [ -f "$input_file" ]; then
        # Process the file
        sed 's/old_pattern/new_pattern/g' "$input_file" > "$output_file"
        echo "Processed $input_file -> $output_file"
    fi
done

Performance Considerations

For large ranges, the C-style loop is generally more efficient than seq because it doesn't create a large list in memory:

# More memory efficient for large ranges
start=1
end=1000000

for ((i=start; i<=end; i++)); do
    # Process without storing all numbers in memory
    echo $i > /dev/null
done

Choose the method that best fits your use case. The seq command is readable and works well for moderate ranges, while C-style loops offer better performance and flexibility for complex iteration patterns.

Published: 2024-03-08|Last updated: 2024-03-08T14:00:00Z

Found an issue?