1. Introduction
Counters play a crucial role in loop constructs in the Bash scripting language. Loop counters in Bash scripting are commonly employed to keep track of iterations or control the number of iterations a loop should execute.
In this guide, we will delve into the techniques of incrementing and decrementing counters within Bash loops.
2. Incrementing Counters in Bash Loops
Incrementing counters in Bash loops is a fundamental operation that often controls the behavior and execution of loops. The two primary methods for performing these operations in Bash are using arithmetic expansion (((...))
) and the let command.
Arithmetic Approach:
One of the efficient ways to handle arithmetic operations, including incrementing counters, is using arithmetic expansion.
You can increment a counter using the ‘+’ operator or the ‘++’ operator. This method provides a straightforward way to adjust the counter value.
counter=$((counter + 1))
counter=$((counter++))
counter=$((counter + 1))
- This expression uses arithmetic expansion to increment thecounter
variable by 1.counter=$((counter++))
- This expression uses the post-increment operation, which increases the value of counter by 1.
The ‘++’ operator comes in handy when we need to increment our counter by a value of one, but in some cases, we might need to change that value which is where the ‘+’ makes more sense.
Note: The ‘++’ operator can be used both before the variable (++counter) and after (counter++). The difference is that the pre-increment (++counter) applies the increment immediately, whereas the post-increment (counter++) change applies after the script goes to the next line.
Using let for increment:
The let command in Bash is used for arithmetic operations. It can be employed to increment (or decrement) counters within loops. It evaluates the arithmetic expression inside the double quotes.
let “counter = counter + 1”
let “counter++”
This command increments the value of the counter variable by 1. Both do the same.
2.1. Using arithmetic for Increment in a Loop
For Loop:
Syntax:
for ((counter=<initial_value>; counter<=<upper_limit>; counter++)); do
#code to execute on each iteration
done
If we want to increment by a value different than 1, then the syntax is as follows:
for ((counter=<initial_value>; counter<=<upper_limit>; counter+=<increment_value>)); do
#code to execute on each iteration
done
Since loops with an increment value different than 1 are rarely used we will be showing an example with the ‘++’ operator. This script uses a for loop in Bash to iterate over a range of numbers.
#!/bin/bash
for ((counter=1; counter<=5; counter++)); do
echo “Iteration: $counter”
done
Here initializes a counter at 1 and increments it by 1 in each iteration until it reaches 5. During each iteration, it prints "Iteration:" followed by the current value of the counter.
Until Loop:
#!/bin/bash
counter=1
until ((counter>5)); do
echo “Iteration: $counter”
((counter++))
done
The script initializes a counter at 1 and then uses an until loop to repeatedly print the iteration number, incrementing the counter after each iteration, until the counter exceeds 5.
2.2. Using let for Increment in a Loop
while loop:
Syntax:
counter=initial_value
while [ $counter -le upper_limit ]; do
#code to execute on each iteration
let “counter++”
done
We can see that in the arithmetic approach instead of using the ((...)) brackets we are using [...]. Apart from that there is no less than or equal ‘<=’ symbol.
Instead of -le operator, we use the following options based on your logic:
- -lt (less than)
- -le (less than or equal)
- -gt (greater than)
- -ge (greater than or equal)
- -eq (equal)
- -ne (not equal).
Example: Here instead of incrementing our counter by using double brackets “((...))”, we use the let command.
#!/bin/bash
counter=1
while [ $counter -le 5 ]; do
echo “Iteration: $counter”
let “counter++”
done
It’s important to note that to achieve the same result as the for loop we need to put our increment as the last line in the while loop. However, if we do want to achieve different results we can move it around.
3. Decrementing Counters in Bash Loops
When it comes to decrementing counters they work in a similar way to incrementing counters. The main difference apart from changing our “++” operator to the “--” operator is that the initial value and upper limit values get swapped. Decrementing counters can be particularly useful when we want to go through an array in the reverse order, or to make a simple countdown as shown in the following examples.
Arithmetic Approach:
The arithmetic approach follows the same methodology except instead of increasing the value of our counter we are decreasing it.
counter=$((counter - 1))
counter=$((counter--))
The ‘--’ operator comes in handy when we need to decrement our counter by a value of one, but in some cases we might need to change that value which is where the ‘-’ makes more sense.
Using let command:
The let command follows the same methodology where we are only required to swap the ‘+’ character with the ‘-’ character
let “counter = counter - 1”
let “counter--”
3.1. Using arithmetic for decrement in a Loop
for loop:
Syntax:
for ((counter=<initial_value>; counter>=<lower_limit>; counter–)); do
#code to execute on each iteration
done
If we want to decrement by a different value than 1 we can use the “-=” operator the following way:
for ((counter=<initial_value>; counter>=<lower_limit>; counter-=<increment_value>)); do
#code to execute on each iteration
done
Example: Here we will be counting down from 5 to 1.
#!/bin/bash
for ((counter=5; counter>=1; counter--)); do
echo “Iteration: $counter”
done
Taking a look at the output we can see that our loop once again iterates 5 times, but this time in a decreasing order.
3.2. Using let for decrement in a Loop
while loop:
Syntax:
counter=initial_value
while [ $counter -ge lower_limit]; do
#code to execute on each iteration
let “counter--”
done
Example:
#!/bin/bash
counter=5
while [ $counter -ge 1 ]; do
echo “Iteration: $counter”
let “counter--”
done
The script starts with a counter set at 5 and uses a while loop to print the iteration number, decrementing the counter by 1 after each iteration, until the counter is less than 1.
Until Loop:
Example:
#!/bin/bash
counter=5
until ((counter<1)); do
echo “Iteration: $counter”
((counter--))
done
The script initializes a counter at 5 and then uses an until loop to print the iteration number, decrementing the counter after each iteration, until the counter falls below 1.
Subshell behavior
Let's try to count the number of lines in myfile.txt that contain the word "hello".
A naive approach might look like this:
#!/bin/bash
COUNTER=0
cat myfile.txt | grep 'hello' | while read LINE
do
let COUNTER++
done
echo "Number of lines with 'hello': $COUNTER"
This script has a flaw due to the subshell behavior in bash. When using a pipe (|
), the right side runs in a subshell. In this script, the while loop runs in a subshell because of the pipes, so the main shell's COUNTER
variable isn't updated. This means that the script will always output 'Number of lines with 'hello': 0', regardless of how many times "hello" appears in the file.
To fix this, you can use process substitution:
#!/bin/bash
COUNTER=0
while read LINE
do
let COUNTER++
done < <(grep 'hello' myfile.txt)
echo "Number of lines with 'hello': $COUNTER"
Comments