1. Introduction

In the shell, iteration is usually done via loops like the for loop. Sometimes, we want to process elements of an array or a general sequence in reverse order.

In this tutorial, we’ll explore a few ways to iterate over a range of elements in reverse via the for loop.

2. Bash Array

To begin with, let’s create an array of strings and iterate over them in reverse:

$ cat reverse_array.sh
countries=("India" "China" "Sri Lanka" "Ukraine" "Romania")
for ((i=${#countries[@]}-1; i>=0; i--))
do
  echo "${countries[$i]}"
done
$ bash reverse_array.sh
Romania
Ukraine
Sri Lanka
China
India

Firstly, we create the array of strings, i.e., $countries. Then, we use the bash syntax ${#countries[@]} to determine the number of elements in the array.

Finally, we use the for loop to index the array in reverse order. In the for loop, we initialize the index variable i to point to the last element in the array. Next, *we decrement this index to zero one by one, outputting the i-th element at every iteration*. In Bash, array indexing starts at 0.

3. Using the tac Command

The tac command is the reverse of the cat command, i.e., it prints the lines of its input in reverse order.

As an example, let’s print the first four users in the /etc/passwd file:

$ cat reverse_tac.sh
for a in $(cat /etc/passwd | head -4 | cut -f1 -d ':') 
do
  echo $a 
done
$ bash reverse_tac.sh
root 
daemon 
bin 
sys

First, we pipe the file /etc/passwd to the head command to read the first four lines. Next, we extract the ID of the user (field 1) with cut and print it in sequence.

Now, let’s print the IDs in reverse order:

$ cat users_reverse.sh
for a in $(cat /etc/passwd | head -4 | cut -f1 -d ':' | tac) 
do
  echo $a 
done
$ bash users_reverse.sh
root 
daemon 
bin 
sys

By piping the output to tac, we’re reversing the list. Next, we’re reading the output using the for loop.

To conclude, a sequence of strings can be reversed using the tac command.

4. Using the seq Range Command

The seq command in Bash allows us to generate a sequence of numbers in reverse order.

Obviously, we can then use the generated indices for printing array elements:

$ cat seq_reverse.sh
countries=("India" "China" "Sri Lanka" "Ukraine" "Romania") 
numelems=$((${#countries[@]}-1)) 
echo "number of elements = $numelems"
for i in $(seq $numelems -1 0) 
do
  echo "${countries[$i]}" 
done
$ bash seq_reverse.sh
number of elements = 4 
Romania 
Ukraine 
Sri Lanka 
China 
India 

As we see above, there are two important concepts. Firstly, we calculate the index of the last element in the array using the arithmetic operator $((${#countries[@]}-1)). Next, we generate the indices in reverse order using the seq command.

Finally, using the generated indices, we print the elements. In summary, the important idea to note is the negative number for the second argument of the seq command when the first argument is greater than the third.

5. Using the tail and head Commands

Now, let’s move to the tail and head commands as a solution:

$ cat lines.txt 
Line 1
Line 2
Line 3
Line 4
Line 5
$ cat file_reverse.sh
numlines=$(cat lines.txt | wc -l) 
for i in $(seq $numlines -1 1) 
do
  head -n $i lines.txt | tail -n +$i 
done
$ bash file_reverse.sh
Line 5
Line 4
Line 3
Line 2
Line 1

First, we print our original file contents. Second, we get the number of lines in the file using the wc -l command. After that, we generate the line numbers in reverse order using the seq command.

Next, the head -n $i command prints the first i number of lines. In essence, the output of the head command is piped to tail -n +$i, which prints starting at the i-th line. *This head and tail pipeline prints only the i-th line, since that’s where the two commands overlap*.

In summary, we used a sequence of the bash commands including tail and head to print the lines of a file in reverse order.

6. Conclusion

In this article, we discussed a few ways of iterating over a list of items in reverse order.

Firstly, we stored the elements in a Bash array and traversed the array in reverse. Second, we used the bash commands like tac and seq to reverse the elements and iterated over the elements. Finally, we looked at a complete example for printing the lines of a file in reverse order using the head and tail commands.