1. Overview

Automation scripts typically involve looping. We can loop through the values of some parameter or a list of files to be processed. Also, we might continue a process while or until some condition is met.

In this tutorial, we’ll look at how to use Bash loops and some specific commands to go through a range of numbers defined by variables.

2. Using for Loop

Basically, a for loop performs a task for a specific set of items while a given condition is true.

Let’s see the basic syntax of a for loop in Bash:

for <name_of_variable> in <list_of_items>; do <perform_some_task> done;

A simple use case involving for loops is number iteration. Using the above syntax, we can iterate over a range of numbers, say 1 to 10:

#!/bin/bash
for i in {1..10}
do
  echo "$i"
done

However, this script won’t work if we set the range using variables. Let’s take an example of the variable range here with the test.sh script:

START=$1
END=$2
for c in {$START..$END}
do
  echo "$c"
done

When we invoke the above script with command line variables, say 6 and 9, the output is simply the range value itself:

$ ./test.sh 6 9
{6..9}

This is because the brace expansion occurs prior to variable expansion in Bash. Thus, brace expansion won’t take variables into consideration.

To overcome this limitation, we can use the for loop as found in conventional programming languages like C. Bash can also leverage this useful construct. It allows us to set a loop’s beginning, increment, and end conditions:

for ((<initial_condition> ; <loop_condition> ; <step_expression>)); do <perform_some_task> done;

For example, we can have a C-style for loop to iterate over a range of numbers:

#!/bin/bash
START=$1
END=$2
if [[ $START =~ ^[+-]?[0-9]+$ ]] && [[ $END =~ ^[+-]?[0-9]+$ ]] && [[ $START -lt $END ]]; then
  echo "The list of numbers for the given range is as:"
  for ((i = $START ; i <= $END ; i++)); do
    echo -n "$i "
    sleep 1
  done
  echo "Done!"
else
  echo "Invalid Range"
fi

The above script takes two command-line arguments $1 and $2 and stores them inside the variables $START and $END.

First, this script checks if the given command line arguments are integers.

Next, it compares them. If the variable $START is smaller than the variable $END, it prints all the numbers in the given range in one-second intervals followed by the message Done!; otherwise, if either the command line arguments aren’t integers or the variable $START is greater than the variable $END, it prints Invalid Range.

Notably, this C-style for loop works in both directions.

3. Using while Loop

The while construct repeats a series of instructions while the condition controlling the loop holds true.

Let’s see an example of using while to iterate over a variable range of numbers:

#!/bin/bash
START=$1
END=$2
n=$START
if [[ $START =~ ^[+-]?[0-9]+$ ]] && [[ $END =~ ^[+-]?[0-9]+$ ]]; then
  echo "The list of numbers for the given range is as:"
  while [[ $n -le $END ]]; do
    echo -n "$n "
    ((n = n + 1))
    sleep 1
  done
else
  echo "Invalid Range"
fi

Mostly, this script is similar to the previous for loop. For example, the if expression checks if the command line inputs are integers. Here, the while loop prints the numbers in the given range when the first command line argument is greater than the second one.

However, the while loop also needs a separate variable ($n), which works like the control variable ($i) of the for loop.

Unlike the Bash-style for loop, the while loop can work in both directions if we modify the driving expression. Also, this loop can work for negative numbers as well.

4. Using seq Command

Another command that we can use for printing numbers in a variable range is seq.

Basically, the seq command prints a series of integers between given starting and ending values, with optional increment stepping. It’s reminiscent of the for loops and range() functions used in various programming languages.

First, let’s see the basic usage of this command. In its simple form, the seq command takes an integer argument:

$ seq 5

The above command prints the numbers from 1 to 5. Let’s give it a start value of 3, an end value of 18, and a step of 2:

$ seq 3 2 18

Consequently, the above command prints all the numbers from 3 to 17 in increments of 2.

Now, let’s use the seq command to iterate over a variable range of numbers:

#!/bin/bash
START=$1
END=$2
if [[ $START =~ ^[+-]?[0-9]+$ ]] && [[ $END =~ ^[+-]?[0-9]+$ ]] && [[ $START -lt $END ]]; then
 echo "The list of numbers for the given range is as:"
  for i in $(seq $START $END); do
    echo -n "$i "
    sleep 1
  done
  echo "Done!"
else
  echo "Invalid Range"
fi

Here, most of the script is similar to the previous ones, except for the format of the for loop. The seq command takes the initial value and the final value as the input arguments. Also, seq doesn’t work if the first argument is larger than the second one.

5. Using eval Command

We can also use the eval command for our purposes. Similar to the seq command, we can supply a variable range to the echo command inside the eval command:

#!/bin/bash
START=$1
END=$2
if [[ $START =~ ^[+-]?[0-9]+$ ]] && [[ $END =~ ^[+-]?[0-9]+$ ]]; then
 echo "The list of numbers for the given range is as:"
  for i in $(eval echo "{$START..$END}"); do
    echo -n "$i "
    sleep 1
  done
  echo "Done!"
else
  echo "Invalid Range"
fi

Unlike the rest of the methods we discussed so far, this approach works for all ranges of integers and prints the numbers in both directions. On the other hand, eval is a very dangerous command and should be used with caution.

6. Conclusion

In this article, we saw several ways to loop over a variable range of numbers.

To summarize, iterating across a variable range of integers is possible in Bash in a few different ways. In most cases, if we don’t consider performance, we can select the one that makes our code most readable.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.