Bash until Loop

By 

Updated on

8 min read

Terminal showing a Bash until loop counting from zero to five

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:

txt
until [CONDITION]
do
  [COMMANDS]
done

The 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:

sh
#!/bin/bash

counter=0

until [ $counter -gt 5 ]
do
  echo Counter: $counter
  ((counter++))
done

The 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:

output
Counter: 0
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5

Notice 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:

Terminal
counter=0; until [ $counter -gt 5 ]; do echo "Counter: $counter"; ((counter++)); done

Other 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:

sh
counter=0

until (( counter > 5 ))
do
  echo "Counter: $counter"
  ((counter++))
done

This 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:

sh
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:

sh
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
done

If the file never appears, the loop stops on the third attempt instead of running forever:

output
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:

sh
n=0

until [ $n -ge 5 ]
do
  ((n++))
  if (( n % 2 == 0 )); then
    continue
  fi
  echo "Odd: $n"
done
output
Odd: 1
Odd: 3
Odd: 5

Practical 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:

sh
#!/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:

output
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:

sh
#!/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:

sh
#!/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:

sh
#!/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:

sh
#!/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 .

PatternDescription
until [ condition ]; do ... doneLoop while condition is false
until command; do ... doneLoop until command succeeds (exit 0)
until (( i > 5 )); do ... doneArithmetic condition without $ or -gt
until [[ $var =~ regex ]]Loop until a regex matches
until false; do ... doneInfinite loop (use break to exit)
until [ -f file ]; do ... doneWait for a file to appear
breakExit the loop immediately
continueSkip 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

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