dotlinux blog

Writing a C-Style Bash for Loop – Example

Bash, the Bourne-Again Shell, is a powerful scripting language used for automating tasks in Unix-like operating systems. One of its most versatile features is loop structures, which allow you to repeat commands efficiently. While Bash offers several loop types (e.g., for-in, while, until), the C-style for loop stands out for its precision in numerical iterations. Inspired by the C programming language, this loop is ideal when you need to iterate over a fixed range of numbers, control step sizes, or execute a command a specific number of times.

In this blog, we’ll demystify the C-style for loop in Bash. We’ll cover its syntax, break down practical examples (from basic to advanced), discuss common use cases, highlight pitfalls to avoid, and share best practices. By the end, you’ll be confident using this loop to solve numerical iteration problems in your scripts.

2026-01

Table of Contents#

  1. What is a C-Style for Loop in Bash?
  2. Syntax of the C-Style for Loop
  3. Basic Example: Counting from 1 to N
  4. Advanced Examples
  5. Common Use Cases
  6. Pitfalls and How to Avoid Them
  7. Best Practices
  8. Conclusion
  9. References

What is a C-Style for Loop in Bash?#

The C-style for loop is a Bash extension (not part of POSIX sh) designed for numerical iteration. Unlike the traditional for-in loop (e.g., for item in list; do ... done), which iterates over a list of strings, the C-style loop uses arithmetic expressions to control iteration. This makes it perfect for scenarios like:

  • Repeating a command a fixed number of times.
  • Iterating over a range of integers (e.g., 1 to 100).
  • Generating sequences with custom step sizes (e.g., even numbers only).

It’s called “C-style” because its syntax mirrors the for loop in the C programming language, using (( ... )) for arithmetic evaluation.

Syntax of the C-Style for Loop#

The syntax of a C-style for loop in Bash is:

for (( initialization; condition; increment/decrement )); do
    # Commands to execute in each iteration
done

Let’s break down each component:

ComponentPurpose
initializationSets the starting value of the loop variable (e.g., i=1).
conditionA logical test that determines if the loop continues (e.g., i <= 10).
increment/decrementUpdates the loop variable after each iteration (e.g., i++, i--).
do ... doneEncloses the commands to run in each iteration.

Key Notes:#

  • Double parentheses (( ... )): These are mandatory for arithmetic evaluation. They tell Bash to interpret the content as an arithmetic expression (not strings).
  • Semicolons ;: Separate the initialization, condition, and increment/decrement blocks.
  • No spaces around =: In arithmetic context, assignment uses i=1 (not i = 1).

Basic Example: Counting from 1 to N#

Let’s start with a simple example: a loop that counts from 1 to 5 and prints each number.

#!/bin/bash
 
# Count from 1 to 5 using a C-style for loop
for (( i=1; i<=5; i++ )); do
    echo "Count: $i"
done

Output:#

Count: 1
Count: 2
Count: 3
Count: 4
Count: 5

Explanation:#

  • Initialization: i=1 starts the loop with i=1.
  • Condition: i <=5 ensures the loop runs as long as i is less than or equal to 5.
  • Increment: i++ increases i by 1 after each iteration (equivalent to i=i+1).

Advanced Examples#

Let’s explore more complex use cases to see the flexibility of C-style loops.

1. Reverse Loop (Counting Down)#

To count from 10 down to 1, reverse the condition and use i-- (decrement):

#!/bin/bash
 
# Count down from 10 to 1
for (( i=10; i>=1; i-- )); do
    echo "Countdown: $i"
done

Output:#

Countdown: 10
Countdown: 9
...
Countdown: 1

2. Custom Step Sizes#

By default, the loop increments by 1 (i++). To use a custom step (e.g., increment by 2 for odd numbers), replace i++ with i+=2:

#!/bin/bash
 
# Print odd numbers between 1 and 10 (step size 2)
for (( i=1; i<=10; i+=2 )); do
    echo "Odd number: $i"
done

Output:#

Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9

3. Using Variables for Start/End Values#

You can use variables to define the start, end, or step size, making the loop dynamic. For example, iterate from a user-defined start to end value:

#!/bin/bash
 
# Define start and end values
start=3
end=7
 
# Loop from $start to $end
for (( i=start; i<=end; i++ )); do
    echo "Iteration: $i"
done

Output:#

Iteration: 3
Iteration: 4
Iteration: 5
Iteration: 6
Iteration: 7

4. Nested C-Style Loops#

You can nest C-style loops to create multi-dimensional sequences (e.g., printing a 3x3 grid of asterisks *):

#!/bin/bash
 
# Nested loop: Print a 3x3 grid of asterisks
for (( row=1; row<=3; row++ )); do       # Outer loop (rows)
    for (( col=1; col<=3; col++ )); do   # Inner loop (columns)
        echo -n "* "                     # Print "* " without newline
    done
    echo ""                              # Newline after each row
done

Output:#

* * * 
* * * 
* * * 

5. Combining with Other Commands#

C-style loops can execute system commands in each iteration. For example, run ping 3 times to test connectivity:

#!/bin/bash
 
# Run ping 3 times using a C-style loop
for (( i=1; i<=3; i++ )); do
    echo "--- Ping attempt $i ---"
    ping -c 1 google.com  # Send 1 ICMP packet to google.com
done

Output (simplified):#

--- Ping attempt 1 ---
64 bytes from 142.250.184.174: icmp_seq=1 ttl=117 time=28.3 ms
--- Ping attempt 2 ---
64 bytes from 142.250.184.174: icmp_seq=1 ttl=117 time=27.9 ms
--- Ping attempt 3 ---
64 bytes from 142.250.184.174: icmp_seq=1 ttl=117 time=29.1 ms

Common Use Cases#

C-style for loops shine in these scenarios:

1. Fixed Iteration Counts#

When you need to run a command exactly N times (e.g., testing a script’s reliability by running it 100 times).

2. Numerical Ranges#

Generating sequences like IP addresses (e.g., 192.168.1.$i for i from 1 to 254) or file names (e.g., file_1.txt, file_2.txt).

3. Array Indexing#

If you’re working with Bash arrays, use C-style loops to iterate over indices (since arrays are zero-indexed in Bash):

#!/bin/bash
 
fruits=("apple" "banana" "cherry")
 
# Iterate over array indices (0, 1, 2)
for (( i=0; i<${#fruits[@]}; i++ )); do
    echo "Fruit $i: ${fruits[$i]}"
done

Output:#

Fruit 0: apple
Fruit 1: banana
Fruit 2: cherry

Pitfalls and How to Avoid Them#

1. Forgetting Double Parentheses#

Mistake: Using single parentheses ( ... ) or no parentheses.
Fix: Always use (( ... )) for C-style loops.

# Wrong: Single parentheses cause syntax errors
for (i=1; i<=5; i++); do echo $i; done  # Error!
 
# Correct: Double parentheses
for (( i=1; i<=5; i++ )); do echo $i; done  # Works

2. Infinite Loops#

Mistake: A condition that never becomes false (e.g., i=1; i>0; i++).
Fix: Ensure the increment/decrement moves the loop variable toward making the condition false.

# Infinite loop (i increases forever, i>0 is always true)
for (( i=1; i>0; i++ )); do echo $i; done  # Avoid!
 
# Fixed: Condition i <= 5
for (( i=1; i<=5; i++ )); do echo $i; done  # Safe

3. Variable Persistence#

Mistake: The loop variable (i) retains its value after the loop ends.
Fix: Explicitly unset the variable if you don’t need it afterward.

for (( i=1; i<=5; i++ )); do :; done
echo "i after loop: $i"  # Output: "i after loop: 6" (since i increments to 6 to exit)
 
unset i  # Clear the variable
echo "i after unset: $i"  # Output: "i after unset: " (empty)

4. Incorrect Operators#

Mistake: Using string comparison operators (e.g., == for assignment).
Fix: In arithmetic context:

  • Use = for assignment (i=1).
  • Use == or = for comparison (e.g., i == 5).
# Wrong: Using == for assignment (arithmetic context uses = for assignment)
for (( i==1; i<=5; i++ )); do echo $i; done  # Error!
 
# Correct: = for assignment
for (( i=1; i<=5; i++ )); do echo $i; done  # Works

Best Practices#

  1. Use Meaningful Variable Names: Prefer count, row, or index over vague names like x or n.
  2. Comment Complex Loops: Explain the purpose of nested loops or non-obvious conditions.
  3. Limit Loop Scope: If the loop variable isn’t needed afterward, unset it to avoid cluttering the environment.
  4. Test with Small Ranges: Debug loops with small values (e.g., 1 to 3) before scaling to 1 to 1000.

Conclusion#

The C-style for loop in Bash is a powerful tool for numerical iteration, offering precision and flexibility for tasks involving fixed ranges, step sizes, or repeated commands. By mastering its syntax ((( initialization; condition; increment ))) and avoiding common pitfalls (like infinite loops or missing parentheses), you can write efficient scripts for automation, testing, and sequence generation.

Practice with the examples above, and experiment with custom ranges and step sizes to build confidence. The C-style loop is a staple in Bash scripting—once you learn it, you’ll wonder how you lived without it!

References#