Bash for Loop Range Variable
Last updated
Last updated
Bash supports for loops to repeat defined tasks, just like any other programming language. Using for loops, we can iterate a block of statements over a range of numbers to achieve the desired outcome. This set of numbers, to be supplied to the for loop, can be generated in multiple ways.
In this tutorial, we will understand different ways of using for loop over a range of numbers.
Every for loop in Bash begins with the keyword 'do' followed by the commands inside the block. Similarly, it is closed by the 'done' keyword. The number of times a 'for loop' runs is dependent on the declared list of variables. After the first execution of the block of code or commands between 'do' and 'done', the loop chooses the next subsequent item from and repeats the complete cycle again.
The for loop can be used with a range of numbers, starting from the specified number, and incrementing or decrementing by a factor, and ending at a given number. The command-line tools like seq and eval can also be injected inside the for loop to generate a range of numbers for the loop to act upon.
We can use the for loop with the range syntax and indicate the first and last element.
Syntax:
Example:
#!/bin/bash
echo
"Forward"for
i in
{1..10}do echo
"$i"done
echo
"Reverse"for
i in
{10..1}do echo
"$i"done
The script outputs all the elements from the provided range using the brace expansion. This syntax works for elements in ascending if the starting number is lesser than the final number. It will work exactly in the opposite way and iterate in descending order if the starting number is greater than the final one. The numbers generated are in arithmetic progression with a common difference of 1 or -1, as per the scenario.
Let’s try to use the same example with variables.
#!/bin/basha=1b=10for i in {$a..$b} #loop 1do echo "$i"done
As you can see, this has a major flaw. We cannot use this with variables. Why is it happening? This is because in bash, brace expansion is always performed before any other expansions. Any character special to other expansions are preserved in the result. Hence, brace expansion is done early as a purely textual macro operation, before parameter expansion.
The brace expansion in the first for loop is happening before $a and $b could be assigned values. As a result, the variable i holds just a single value "{$a..$b}" making this loop run once. While echoing the variables $a and $b are then expanded, which prints "{1..10}".
From Bash Bash version 4.0+ we have the option to use the above method with a step count.
Syntax:
Example:
#!/bin/bash
echo
"Forward"for
i in
{1..10..2}do echo
"$i"done
echo
"Reverse"for
i in
{10..1..2}do echo
"$i"done
Using the increment or the decrement factor, we are able to make the loop iterate over a particular set of numbers in the range. If that factor is set to 1, we will achieve the same result as we did earlier. This approach can’t still be applied to variables as brace expansion will happen before the parameters could expand.
The script is modifying the numbers in steps of 2. In the first iteration, the for loop takes the starting number and executes the statements inside the do-done block. In the next iteration, it will add or subtract the factor from the starting number and repeat those executions. This will continue until the iterator (i) is greater or lesser than the ending number
We can specify the variable range to the echo command and this will be inside the eval command.
Syntax:
Example:
#!/bin/bash
echo
"Forward"for
i in
$(eval
echo
"{1..10}")do echo
"$i"done
echo
"Reverse"for
i in
$(eval
echo
"{10..1..3}")do echo
"$i"done
echo
"With variables"a=2b=8c=2for
i in
$(eval
echo
"{$a..$b..$c}")do echo
"$i"done
This approach works with the help of the eval command. eval provides an additional layer of evaluation before executing the command. echo {$a..$b..$c} will be evaluated as a command by the eval.
The shortcomings of not being able to use variables, as discussed in the above examples, are handled. This way we can avoid the problems faced with brace expansion and use it for ranges of integers and even use the numbers in both directions.
The iterator is getting assigned the value by the for loop. Inside this loop there is an eval command that which effectively breaks down the given expression to: for i in 1 2 3 4 5
After this, it is a simple for loop which works by picking the elements one by one and executing the statements or commands inside the do-done block.
The seq command is short for sequence. It allows us to generate sequences of numbers within the given range. These numbers can be integers or floating point. We can embed this command inside the for loop.
Syntax:
Example:
#!/bin/bash
echo
"Forward"for
i in
$(seq
1 10)do echo
"$i"done
echo
"With variables"a=2b=10c=2.5for
i in
$(seq
$a $c $b)do echo
"$i"done
The seq command helps to iterate over a range of numbers. The seq command can take the first, the factor and the final value as the input arguments. One important thing to note, which is also a major drawback, is that this command will not work if the first argument is larger than the final one.
We have developed a Bash script that uses the seq command to generate numbers for the loop. The sequence numbers can go in either direction. We can have used it with variables and took the step factor as a decimal number. The script in the final for loop generated numbers from 2 to 10 with an increment of 2.5 every time.
This is a very common method, present in every programming language.
Syntax:
Where,
exp1 = initializer
exp2 = condition
exp3 = counter
Example:
#!/bin/bash
echo
"Forward"for
(( i=1; i<=10; i++ ))do echo
"$i"done
echo
"Reverse"for
(( i=10; i>=1; i-- ))do echo
"$i"done
The initial value of the iterator (i) is set to the starting number in the range. This is the first expression, exp1. The loop executes, as long as the condition in exp2 is true, which signifies that the maximum value cannot be more than the last number of the range. The exp3 corresponds to the step factor that increments or decrements it.
The script begins by initializing i=1. It checks the current value of i against 10. As it is less, the statement inside the do-done block executes, after which the i++ operation increments i to 2. As i is less than equal to 10, the same cycle repeats till 10. The moment i has been incremented to 11, the loop terminates.
Bash gives us different ways to iterate over a variable range of numbers.
We saw the drawbacks of the approaches and learned how to handle it.
We can automate different processes using loops over a numerical range.