Getting started with until vs while loop in Bash
A while loop in bash works similarly to those in other programming languages such as C, Python, etc. A while loop will evaluate a condition and execute the body until the condition fails. An until loop is the opposite, because it will evaluate the condition and execute the body until the condition succeeds.
For beginners or people starting with programing language can easily get confused on which loop to use. So we will cover some examples to explain the comparison and difference between until vs while loop with syntax and examples.
Loop Structure (psuedocode syntax)
while condition; do body done until condition; do body done
Conditionals
For while loops and until loops, a condition can be any command. Bash evaluates the exit code of the command. If the exit code is 0, bash considers that a success, and anything else is a failure.
Usually you'll want to use either single or double square-brackets for your conditional. Single brackets are compatible with other POSIX shells but if you know you'll only be running this on hosts that have bash, you should use double square-brackets, to take advantage of additional features. Consult the bash manual for more details.
Lab Environment
All code you see here was executed in a RockyLinux VirtualBox VM. But everything shown is generic enough to run on any modern Linux distribution. You may need to install nc ("netcat") for example #2 where we would check the connectivity towards our webserver. This is completely optional and required only to cover our example usecase.
# for debian-based distributions
sudo apt install nc
# for redhat-based distributions
sudo dnf install nc
How until and while loop are different?
The main difference between until and while loop is that:
- The while executes a piece of code if the control expression is true, and only stops when it is false (or a explicit break is found within the executed code.
- The until loop is almost equal to the while loop, except that the code is executed while the control expression evaluates to false.
Let me explain using a small infinite loop using while and until loop:
Using while loop:
#!/bin/bash
while true; do
echo "Exit code: $?"
echo "sleeping"
sleep 1
done
Here in the output you can see the exit code of the while loop is 0 which means the control expression is true:
Exit code: 0
sleeping
Exit code: 0
sleeping
Exit code: 0
sleeping
Let's try the same using until loop:
#!/bin/bash
until ((0)); do
echo "Exit code: $?"
echo "sleeping"
sleep 1
done
Here in the output as you see the exit code is non-zero so our code will continue to execute until the condition is false.
Exit code: 1
sleeping
Exit code: 1
sleeping
Exit code: 1
sleeping
Exit code: 1
sleeping
Exit code: 1
sleeping
Example 1: Incrementing a variable
In this example we test if the value of variable i
is less than 10
. The body of the loop increments the value with "i=$((i+1))
" so it executes the echo statement 10 times.
#!/bin/bash
i=0
while [[ "$i" -lt 5 ]]; do
echo "i = $i"
i=$((i+1))
done
Output:
[me@localhost ~]$ ./ex1.sh
i = 0
i = 1
i = 2
i = 3
i = 4
[me@localhost ~]$
Now let's achieve the same using until loop. So as we understand, we just need to negate the check as used in while loop. So instead of using less that (lt) we will use greater than (gt) with until loop to achieve the same output:
#!/bin/bash
i=0
until [[ "$i" -gt 5 ]]; do
echo "i = $i"
i=$((i+1))
done
Instead of using greater than, we could have used less than similar to while loop but with a NOT (!) condition as shown below:
#!/bin/bash
i=0
until [[ ! "$i" -lt 5 ]]; do
echo "i = $i"
i=$((i+1))
done
Both the code will give the same output as we got with while loop.
Example 2: Wait for webserver to start (until)
The nc command is useful for network debugging. You can send plaintext queries for SMTP, HTTP, and for checking if ports are open like in this example. With the "-z" option nc will return 0 if it sees that the port is open and will return 1 otherwise.
It's more idiomatic to use the until loop in this case because you want to keep checking the condition "until" it succeeds. The next example shows the equivalent with a while loop instead.
Code:
#!/bin/bash
until nc -z "$1" 80; do
sleep 1
done
echo "webserver is running"
Output:
[me@localhost ~]$ ./ex2.sh localhost
webserver is running
[me@localhost ~]$
Example 3: Wait for webserver to start (while)
Here's the equivalent with while. The only difference is how the condition was negated. The output will be the same.
#!/bin/bash
while ! nc -z "$1" 80; do
sleep 1
done
echo "webserver is running"
Output:
[me@localhost ~]$ ./ex3.sh localhost
webserver is running
[me@localhost ~]$
Example 4: Iterate through lines in a file
A helpful construct is using the read command to read a file line-by-line, that way you can perform some kind of operation on each line. In this case we're only incrementing our num_lines
variable each time the loop executes.
It makes more sense to do this example with a while loop because we want to perform an action every time read succeeds. We could of course negate the condition and use an until loop, replacing the "while" with "until !". But this would look less readable.
#!/bin/bash
if [[ "$#" -ne 1 || ! -f "$1" ]]; then
echo "please profile a file as an argument"
exit 1
fi
num_lines=0
while IFS= read -r line; do
num_lines=$((num_lines+1))
done < "$1"
echo "there were lines $num_lines in $1"
Output:
[me@localhost ~]$ ./ex4.sh ./file.txt
there were lines 115 in ./file.txt
[me@localhost ~]$
The same could by written using until with a NOT condition as shown below:
num_lines=0
until ! IFS= read -r line; do
num_lines=$((num_lines+1))
done < "$1"
Conclusion
Use a while loop when you want to keep doing a task while a condition returns true, and an until loop if you want to keep doing a task until a condition returns true. Any while loop can be written as an until loop or vice-versa, by negating the condition. Use whichever you believe enhances the readability of your program.
Further Reading