Quick Links

Most programming languages support various forms of looping code lines. Bash natively supports 'while' loops, 'until' loops and the most well-known, 'for' loops. This article introduces and discusses all three.

What Are Bash Loops?

To define this a little better, we should start with the question what are loops. Loops are a programming language construct, allowing a developer to repeat (i.e. loop) certain parts, or all, of the code inside such a loop definition. It is now easy to define Bash Loops as any loop programming language construct used in Bash!

Bash natively supports 'for', 'until' and 'while' based loops. Each of these has their own advantages, but you can already get a sense of their meanings just by looking at the main word idiom. For example, 'until' leads one to naturally think about 'do something until' and this indeed what a Bash 'until' loops does; it loops a certain amount of (or all) code until a certain condition has been met.

Similarly, 'while' loops keep running until a condition is no longer true. Finally, 'for' loops loop, for example, a defined number of times, similar to how we would write 'for 70 times, do...'. This helps us to logically understand the unique features that each loop provides us with and to implement more readable code.

for Based Bash Loops

For the purposes of this article, we will have a look at the newer form of defining Bash for loops. A somewhat older, less modern, definition of for loops in Bash may, for example, look something like this:

        for i in $(seq 1 5); do echo $i; done
    

. Let's compare this with a cleaner, better structured, modern looking for loop:

for ((i=1;i<=5;i++)); do echo $i; done

A Bash for based loop

This simple for based Bash one-liner (a term often used in Linux/Bash circles to conceptualize a mini-script written on a single line) will print the numbers 1 to 5 in sequential order. We set a start value for the i variable ($i) by assigning the value 1 to the same, as the first part of our for loop definition, terminated by a ; delimiter.

Next, we specify that we only one to go up to 'smaller, or equal, to five' by using i<=5. We then indicate what should happen at the end of each round, and that is increase the variable i by one, or, in a commonly used (including in the C++ language for example) coding shorthand, this renders as i++.

Finally we specify the start of our loop code by using do, that is, after terminating our for loop definition, alike to any other statement termination, with ; before the do. We also specify the end of our loop code by using done and we echo (print) the value of our variable i in between the do and done.

Also specifically note that the do clause itself is not terminated with ;, and doing so would lead to an error. Consider the do a prefix to what needs to be done, and it makes more sense. This perhaps one reason why at times it is cleaner to put things into a multi-line script, as the do can simply be the last word on a line.

Even when defining other types of loops, we will still maintain the do and done clauses, and we will always make sure to terminate our loop definition (i.e. before do) with ;, as well as terminating the end of each individual statement within our do...done loop definition with ;.

Let's put this into a small script to see more clearly how things can work that way:

#!/bin/bash

for ((i=1;i<=10;i+=2)); do

echo $i

done

A Bash for based loop script

After making the script executable with chmod +x test.sh, we execute the same. A few small changes were introduced to the script. Note how this time, we are increasing the variable i by two each time. This done using another coding shorthand, namely i+=2 which can be read as increase i by two. One can also write i=i+2 in the same place, and it works exactly the same.

We see that we start at 1, and increase by 2 each time we go through the loop, ending at 9. The reason it ends at 9 is that the maximum value is 10. Thus, after 9 the next value would be 11, which is greater then 10 and it is thus not displayed/looped.

Note also how the ; was removed after the echo line. This because there is no need to terminate the end of a statement if there is an end-of-line situation/character instead. This the case here; we do not have any other command after the echo $i and the line immediately ends (spaces at the end of the line would be fine too, the principle is simply not to have another command unless that new command is prefixed (and the previous one terminated) by ;).

while Based Bash Loops

Let us next have a look at a Bash loop - using the same do...done loop definition, we can define a while based Bash loop which will run as long as a given condition is true.

i=1; while [ $i -le 5 ]; do echo $i; i=$[ i + 1 ]; done

A Bash while based loop

In this example, we do the same as our first for based loop example. Whilst the definition looks more complex (and thus a for loop may be better suited for this particular use case), it is interesting to see how we can define a while loop in the same way.

Here we first set our i variable, manually, in a separate command terminated by ;, to 1. Next we start a while loop where we set a condition, in a very similar way to defining an if statement (there is a link at the end of this article to an article about if statements if you like to learn more), where we are checking if the i variable is lower or equal (-le) then 5.

After this we have our usual do...done block in which we echo our variable, and subsequently, in a new statement, manually, increase the value of our i variable by one in a mathematical calculation as defined by the $[...] Bash calculation idioms. Let's next checkout an until based Bash loop

until Based Bash Loops

Armed with what we learned so far, we can now more readily evaluate the following until based Bash loop:

i=1; until [ $i -gt 5 ]; do echo $i; i=$[ i + 1 ]; done

A Bash until based loop

Here we look for the condition i greater then 5 to become true. Until such time (i.e. an until based loop), we will print (using echo) our variable i and increase the same with one, alike to our previous while based example.

We can see how the syntax for the until command is very similar to the while command. Also note that, unlike the for based command, these commands are looking for either a true condition to persist (with while), or a for a true condition to commence (with until). This also allows one to use other commands which can return a true/false like output, like for example grep -q:

echo 1 > a

while grep -q '1' ./a; do echo 'yes'; sleep 5; done

Checking for a true status using grep -q in a while based Bash loop

Here we add the number 1 to a file named a, and check for the existence of the number 1 within that file using a grep -q (a quiet grep). We keep doing so (i.e. while) until it is no longer true. Whilst it is true, we print the text yes and pause the loop for five seconds with sleep 5. Note how each command is terminated with ; again.

After about 17 seconds we interrupt our script by using CTRL+c, which is the universal method to stop a running process in Bash (besides the stronger and more effective CTRL+z which pauses a process immediately, but that is for another article!)

Wrapping up

In this article, we reviewed 'for', 'while' and 'until' loops which are natively available in Bash. If you are interested in more Bash, have a look at Conditional Testing in Bash: if, then, else, elif and Bash Functions and Local Variables.

Enjoy!