In Bash, while and until are the same mechanism with the condition
flipped. The test is always “run a command, look at its exit status”: 0 means
success, non-zero means failure. A while loop runs the body while the
command succeeds; an until loop runs the body until the command succeeds
(that is, while it still fails). Anything you write with one can be written with the
other by negating the test—use whichever form matches how you read the condition aloud.
Tested all the commands and code from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.
For more patterns (infinite loops, nested loops, pitfalls), see bash while loop.
Bash until vs while: exit status rules
| Loop | Body runs when the condition command… |
|---|---|
while CMD |
exits 0 (true / success) |
until CMD |
exits non-zero (false / failure) |
So while true loops forever, and until false does the same. until true runs the body zero times because the condition is already successful
before the first iteration:
while true; do echo one; break; done
until true; do echo never; done
echo after_until_trueOutput:
one
after_until_trueEquivalence you can rely on in reviews and refactors:
while CMDbehaves likeuntil ! CMDuntil CMDbehaves likewhile ! CMD
(Here ! is shell negation of the exit status, not arithmetic.)
While do loop bash and bash do until: syntax
Order matters: it is while CONDITION; do, not “while do” first—the keyword
do opens the body, and done closes it. until is the same shape.
while CONDITION; do
# body
done
until CONDITION; do
# body
doneCONDITION can be any command or pipeline Bash allows in that position; in practice
most scripts use [[ … ]] for Bash-only hosts, or [ … ] when you care
about POSIX sh. Details live in the
Bash manual on looping constructs.
Bash until loop vs bash while loop: same counter
Count from 0 to 4 with while: keep going while the value is still
strictly less than 5.
i=0
while [[ "$i" -lt 5 ]]; do
echo "i = $i"
i=$((i + 1))
doneThe until version keeps going while “greater or equal than 5” is false,
so it stops once i reaches 5:
i=0
until [[ "$i" -ge 5 ]]; do
echo "i = $i"
i=$((i + 1))
doneOutput (either script):
i = 0
i = 1
i = 2
i = 3
i = 4You could also negate a while-style test inside until (for example
until [[ ! "$i" -lt 5 ]]), but that is usually harder to read than picking a
direct comparison such as -ge.
Bash until: wait until something becomes true
Retries—“bash loop until the service answers”, “until bash sees the file”—are
often clearer as until, because the thing you care about is success of one
command, and you want to keep trying until that command finally exits 0.
Self-contained example: wait until a file your deploy script expects shows up (here a background subshell creates it shortly after start):
path=$(mktemp -u)
(sleep 0.12; : >"$path") &
until [[ -f "$path" ]]; do
sleep 0.02
done
echo "file appeared"
rm -f "$path"
waitOutput:
file appearedIf you prefer while, negate the test: while ! [[ -f "$path" ]]; do …. Same
behavior; choose the line that matches how you describe the problem out loud.
For TCP ports, many people keep a until nc -z host port (or while ! nc -z …) loop; that needs nc installed and something actually listening, so treat
it as the same pattern as the file wait with a different condition command.
While read: line-by-line input
Reading lines with read succeeds until EOF. The usual pattern is while … read;
you can write an until ! read variant, but it is harder to read for most people.
f=$(mktemp)
printf 'a\nb\nc\n' >"$f"
num_lines=0
while IFS= read -r line || [[ -n $line ]]; do
num_lines=$((num_lines + 1))
done <"$f"
echo "lines=$num_lines"
rm -f "$f"Output:
lines=3Use IFS= read -r so leading or trailing whitespace is not trimmed and
backslashes are not treated as escapes. The || [[ -n $line ]] guard handles a
last line that has no trailing newline.
Summary
while keeps going while the condition command exits 0; until keeps going while
it exits non-zero, and stops once it succeeds. You can always rewrite one form as
the other by negating the test (while ! … vs until …). Use until when the job is
“retry until this check passes,” while when it is “keep going while this stays true,”
and while read when you process a file line by line.
When the stop condition is a wall-clock deadline (for example “run until 17:00” or “run for five minutes”), run a while loop until a specific time in Bash collects the usual sleep / date / timeout patterns.
Further reading:

