Bash until Loop

The until loop in Bash executes a set of commands repeatedly as long as a given condition evaluates to false. Once the condition becomes true, the loop stops.
In Bash scripting, there are three basic loop constructs: the for loop
, the while loop
, and the until loop. The until loop is the inverse of while: it runs while the condition is false rather than true.
This guide explains the syntax of the until loop and demonstrates its use with practical examples.
Syntax
The Bash until loop takes the following form:
until [CONDITION]
do
[COMMANDS]
doneThe condition is evaluated before each iteration. If it evaluates to false (non-zero exit status), the commands inside the loop body run. If it evaluates to true (zero exit status), the loop terminates and control passes to the next command after done.
Basic Example
In the following example, the loop prints the current value of counter and increments the variable
by one on each iteration. The condition uses the -gt comparison operator
to check if counter is greater than 5:
#!/bin/bash
counter=0
until [ $counter -gt 5 ]
do
echo Counter: $counter
((counter++))
doneThe loop runs as long as counter is not greater than 5. Once counter reaches 6, the condition [ 6 -gt 5 ] evaluates to true and the loop stops. The output is:
Counter: 0
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5Notice that the loop body never runs with counter set to 6. The condition is checked before each iteration, so the value that finally makes the condition true is never printed.
When you are working directly in the terminal, you can write the same loop on a single line by separating the keywords with semicolons:
counter=0; until [ $counter -gt 5 ]; do echo "Counter: $counter"; ((counter++)); doneOther Condition Forms
The examples so far use [ ] (single-bracket test). Bash also accepts (( )) for arithmetic evaluation and [[ ]] for string patterns and regular expressions.
Use (( )) when the condition is purely numeric. Variables inside (( )) do not need the $ prefix, and the comparison operators are the familiar <, >, and >= rather than -lt, -gt, and -ge:
counter=0
until (( counter > 5 ))
do
echo "Counter: $counter"
((counter++))
doneThis produces the same output as the previous example, so pick whichever form reads better to you.
Use [[ ]] with the =~ operator when you want to keep looping until a value matches a regular expression. The following script keeps prompting until the reply is a single y or Y:
reply=""
until [[ $reply =~ ^[Yy]$ ]]
do
read -r -p "Continue? [y/N] " reply
done
echo "Confirmed."Any other answer leaves the condition false, so the prompt repeats until the reader types an accepted value.
break and continue Statements
The break and continue statements
let you alter the flow of an until loop without waiting for the condition to change.
The break statement terminates the loop immediately. This is the usual way to put a ceiling on a loop that waits for something that may never arrive:
attempt=0
until [ -f /tmp/ready.flag ]
do
((attempt++))
if [ $attempt -ge 3 ]; then
echo "Giving up after $attempt attempts."
break
fi
echo "Attempt $attempt ..."
sleep 1
doneIf the file never appears, the loop stops on the third attempt instead of running forever:
Attempt 1 ...
Attempt 2 ...
Giving up after 3 attempts.The continue statement skips the rest of the current iteration and jumps back to the condition. Here we count up to five but print only the odd numbers:
n=0
until [ $n -ge 5 ]
do
((n++))
if (( n % 2 == 0 )); then
continue
fi
echo "Odd: $n"
doneOdd: 1
Odd: 3
Odd: 5Practical Examples
Waiting for a Git Host
The following script retries git pull until it succeeds. This is useful when a git host has downtime and you do not want to keep typing the command manually:
#!/bin/bash
until git pull &> /dev/null
do
echo "Waiting for the git host ..."
sleep 1
done
echo -e "\nThe git repository is pulled."The sleep
command pauses for one second between attempts. Once the repository is pulled successfully, the script prints a confirmation message:
Waiting for the git host ...
Waiting for the git host ...
Waiting for the git host ...
The git repository is pulled.Waiting for a Service to Start
This script waits until a service is listening on a specific port before continuing:
#!/bin/bash
echo "Waiting for port 3306 to become available..."
until nc -z localhost 3306 2>/dev/null
do
sleep 1
done
echo "MySQL is ready."The -z option tells nc to scan for a listening service without sending any data. Minimal container images often ship without netcat, and the available options differ between netcat implementations. When you cannot rely on nc being installed, Bash can open the connection itself through the /dev/tcp pseudo-device:
#!/bin/bash
echo "Waiting for port 3306 to become available..."
until (exec 3<>/dev/tcp/localhost/3306) 2>/dev/null
do
sleep 1
done
echo "MySQL is ready."The redirection succeeds only when something accepts the connection, so the loop exits on the first successful attempt. Running it in a subshell keeps file descriptor 3 from staying open in your script. This form is specific to Bash and does not work in sh or dash.
Retry with Maximum Attempts
To avoid an infinite loop, you can add a retry counter that exits after a set number of attempts:
#!/bin/bash
max_retries=10
attempt=0
until curl -s http://localhost:8080/health > /dev/null
do
((attempt++))
if [ $attempt -ge $max_retries ]; then
echo "Service did not start after $max_retries attempts."
exit 1
fi
echo "Attempt $attempt of $max_retries..."
sleep 2
done
echo "Service is up."Countdown Timer
A simple countdown using until:
#!/bin/bash
seconds=10
until [ $seconds -eq 0 ]
do
echo "$seconds..."
((seconds--))
sleep 1
done
echo "Done!"Quick Reference
For a printable quick reference, see the Bash cheatsheet .
| Pattern | Description |
|---|---|
until [ condition ]; do ... done | Loop while condition is false |
until command; do ... done | Loop until command succeeds (exit 0) |
until (( i > 5 )); do ... done | Arithmetic condition without $ or -gt |
until [[ $var =~ regex ]] | Loop until a regex matches |
until false; do ... done | Infinite loop (use break to exit) |
until [ -f file ]; do ... done | Wait for a file to appear |
break | Exit the loop immediately |
continue | Skip to the next iteration |
Troubleshooting
The loop never terminates
Nothing inside the loop body changes the value the condition tests. Make sure the counter is incremented or the command being retried can actually succeed, and add a break with a maximum attempt count when the loop waits on something external.
integer expression expected error
The variable used with -gt, -ge, or -lt is empty or holds non-numeric text. Initialize counters before the loop, for example counter=0, and check that any value read from input or command output is a number.
The loop is skipped, or reports unary operator expected
An unquoted empty variable disappears before [ ] sees it. Written as until [ -n $reply ], an empty reply leaves [ -n ], which tests the non-empty string -n and returns true, so the loop never runs. Written as until [ $reply = "y" ], it leaves [ = "y" ] and fails with unary operator expected. Quote the variable, as in until [ -n "$reply" ], so the test still receives an argument when the value is empty.
The condition works in the terminal but fails in the script
The (( )) and [[ ]] forms are Bash features. A script that starts with #!/bin/sh runs under a POSIX shell where they are not available, so use #!/bin/bash on the first line.
FAQ
What is the difference between while and until?
The while loop
runs as long as the condition is true. The until loop runs as long as the condition is false. They are logical inverses of each other: until [ condition ] is equivalent to while [ ! condition ].
Can I use until with command exit status instead of a test condition?
Yes. The until loop checks the exit status of whatever follows until. Any command that returns a non-zero exit status (failure) keeps the loop running. When the command returns zero (success), the loop stops.
How do I create an infinite until loop?
Use until false; do ... done. The false command always returns a non-zero exit status, so the condition never becomes true. Use break inside the loop to exit when needed.
How do I avoid an infinite loop when waiting for something?
Add a retry counter that increments on each iteration and use break or exit when the maximum number of attempts is reached. See the Retry with Maximum Attempts
example above.
Conclusion
The until loop repeats commands as long as a condition is false, making it ideal for waiting on external events like service availability or network connectivity. For the inverse behavior, use the while loop
.
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page