Loops are one of the fundamental building blocks of Bash scripts. They let us process files, iterate over arrays, wait for conditions, and repeatedly execute commands.
But sometimes you don't want a loop to run until its normal condition says “stop.”
You may find what you were looking for, receive a special command from the user, encounter an error, or simply decide that there is nothing more to do.
For those situations, Bash provides the break builtin.
break
It immediately terminates the current loop and execution continues with the command following that loop.
For example:
for number in 1 2 3 4 5; do
echo "Number: $number"
if [[ $number -eq 3 ]]; then
break
fi
done
echo "Loop finished"
The output is:
Number: 1
Number: 2
Number: 3
Loop finished
The values 4 and 5 are never processed.
Which loops support break?
Bash allows break inside:
forwhileuntilselect
The syntax is:
break [n]
Without an argument, break leaves the innermost enclosing loop.
The optional n is useful with nested loops and specifies how many loop levels should be terminated.
For example:
break 2
leaves the current loop and one enclosing loop.
Using break with for
One of the most common uses is stopping a search once the desired item has been found.
For example:
for number in 1 2 3 4 5 6 7 8 9 10; do
echo "Checking $number"
if [[ $number -eq 5 ]]; then
echo "Found it!"
break
fi
done
echo "Search finished"
The loop doesn't need to examine the remaining numbers after 5 has been found.
This pattern is especially useful when the operation inside the loop is expensive:
for server in "${servers[@]}"; do
if check_server "$server"; then
echo "Available server: $server"
break
fi
done
The loop checks servers until the first successful result.
Searching for a file
Here's another practical example:
for directory in /tmp /var/tmp /opt /usr/local; do
if [[ -f "$directory/config.txt" ]]; then
echo "Found configuration in $directory"
break
fi
done
Once config.txt has been found, continuing to search the other directories is unnecessary.
This is a good example of why break exists: the loop's normal list of items is still there, but the program has already achieved its goal.
Using break with while
break is particularly convenient with while loops used for interactive input.
while true; do
read -r -p "Enter something (q to quit): " input
if [[ $input == q ]]; then
break
fi
echo "You entered: $input"
done
echo "Goodbye!"
Here:
while true
creates a loop that has no natural stopping condition.
The break provides the exit point when the user enters q.
This is a common Bash pattern:
while true; do
# do some work
if [[ some_condition ]]; then
break
fi
done
It is particularly useful when the condition that terminates the loop can only be discovered after performing some work.
break with until
The same mechanism works with until:
count=1
until false; do
echo "Count: $count"
if [[ $count -eq 5 ]]; then
break
fi
((count++))
done
Although this example is deliberately simple, the same pattern can be useful when the termination condition is discovered dynamically.
break with select
Bash's select construct is commonly used to create simple interactive menus.
For example:
select choice in Start Stop Quit; do
case "$choice" in
Start)
echo "Starting..."
;;
Stop)
echo "Stopping..."
;;
Quit)
echo "Goodbye!"
break
;;
*)
echo "Invalid choice"
;;
esac
done
select repeatedly displays the menu until something terminates it.
Here, choosing Quit executes:
break
which ends the select loop.
Notice that case and break have different jobs.
The case statement chooses what to do with the selected value:
case "$choice" in
while break terminates the surrounding loop.
The ;; at the end of a case branch does not terminate the loop.
Nested loops
The default behavior becomes important when loops are nested.
Consider:
for outer in 1 2 3; do
for inner in a b c; do
echo "$outer $inner"
break
done
done
The result is:
1 a
2 a
3 a
The inner loop stops after its first iteration, but the outer loop continues.
Conceptually:
Outer loop
└── Inner loop
└── break
break affects the innermost loop containing it.
Breaking out of multiple loops
When nested loops are involved, the optional numeric argument becomes useful.
for outer in 1 2 3; do
for inner in a b c; do
echo "$outer $inner"
if [[ $outer -eq 2 && $inner == b ]]; then
break 2
fi
done
done
echo "All loops finished"
When the script reaches:
2 b
this executes:
break 2
Both loops are terminated, and execution continues with:
echo "All loops finished"
With three nested loops:
for a in ...; do
for b in ...; do
while condition; do
break 3
done
done
done
break 3 exits all three enclosing loops.
This can be useful, but large values such as break 3 or break 4 can make control flow difficult to understand. If nested loops become deeply complicated, restructuring the code into functions is often clearer.
break and continue
break and continue are easy to confuse because both affect loop execution.
break ends the loop:
for number in 1 2 3 4 5; do
if [[ $number -eq 3 ]]; then
break
fi
echo "$number"
done
Output:
1
2
continue, on the other hand, skips the rest of the current iteration and moves to the next one:
for number in 1 2 3 4 5; do
if [[ $number -eq 3 ]]; then
continue
fi
echo "$number"
done
Output:
1
2
4
5
So the mental model is simple:
break → stop the loop
continue → skip this iteration
break, return, and exit
There are several Bash commands that leave different levels of execution.
Consider this hierarchy:
shell
└── script
└── function
└── loop
The commands operate at different levels:
break → leave the loop
return → leave the function
exit → terminate the script/shell
For example:
find_server() {
for server in "${servers[@]}"; do
if check_server "$server"; then
echo "Found: $server"
break
fi
done
echo "Search complete"
}
Here, break leaves the for loop, but the function continues:
echo "Search complete"
If you wanted to leave the function immediately instead, you would use return:
find_server() {
for server in "${servers[@]}"; do
if check_server "$server"; then
echo "Found: $server"
return 0
fi
done
return 1
}
And exit would terminate the script itself:
exit 0
Choosing the right command is important because each one affects a different level of control flow.
A practical command loop
A useful real-world pattern combines while, case, and break.
while true; do
read -r -p "server> " command
case "$command" in
start)
echo "Starting server..."
;;
stop)
echo "Stopping server..."
;;
status)
echo "Server is running."
;;
quit|exit)
echo "Leaving..."
break
;;
*)
echo "Unknown command: $command"
;;
esac
done
The loop continues processing commands until the user enters either:
quit
or:
exit
At that point:
break
returns execution to the command following the while loop.
This structure is useful for simple command-line interfaces, menus, interactive utilities, and administration scripts.
Multiple termination conditions
A loop may have more than one reason to stop.
For example:
while true; do
read -r input
if [[ $input == quit ]]; then
break
fi
if [[ $input == error ]]; then
echo "Stopping because an error was received."
break
fi
process "$input"
done
The loop has two possible termination paths.
You can also use break when a command fails:
while true; do
if ! process_item; then
echo "Processing failed."
break
fi
done
This can be much clearer than trying to encode every possible condition into the while expression itself.
When not to use break
break is useful, but it shouldn't become a substitute for designing a clear loop condition.
For example, this:
while [[ $count -lt 10 ]]; do
echo "$count"
((count++))
done
already clearly describes when the loop ends.
There is no need to turn it into:
while true; do
echo "$count"
((count++))
if [[ $count -ge 10 ]]; then
break
fi
done
Both work, but the first version expresses the loop's purpose directly.
break becomes particularly useful when the termination condition is discovered inside the loop or when there are several independent ways to stop.
A common mistake: using break outside a loop
break needs an enclosing loop.
This is invalid:
#!/usr/bin/env bash
echo "Hello"
break
Bash reports an error similar to:
bash: break: only meaningful in a `for', `while', or `until' loop
If you are not inside a loop, break is not the appropriate way to leave the current execution context.
Use return inside a function or exit when you actually want to terminate the script.
Checking the exit status
A successful break normally returns status 0.
For example:
while true; do
break
done
echo "$?"
prints:
0
You generally won't need to test the status of break itself, but this becomes relevant when writing scripts that carefully track command failures and return codes.
Keep nested control flow readable
break 2 is useful, but don't forget that someone else may eventually have to understand your code.
Compare:
for server in "${servers[@]}"; do
for port in "${ports[@]}"; do
if check_port "$server" "$port"; then
echo "Found: $server:$port"
break 2
fi
done
done
with a more structured approach using a function and a return value.
The first version is perfectly valid, but break 2 requires the reader to know exactly how many loops surround it.
A little control flow is helpful.
A control-flow escape plan worthy of a space shuttle is probably a sign that the function needs splitting.
Bash documentation
break is a Bash builtin, so you can read its documentation directly from Bash:
help break
You can also inspect the related commands:
help continue
help return
help exit
This is often the quickest way to check the exact syntax available in the Bash version you're using.
Quick reference
The basic command:
break
leaves the current loop.
With nested loops:
break 2
leaves two loop levels.
And:
break 3
leaves three.
Typical usage:
for item in "${items[@]}"; do
if [[ condition ]]; then
break
fi
done
Interactive loop:
while true; do
read -r input
[[ $input == quit ]] && break
process "$input"
done
Nested loops:
for a in ...; do
for b in ...; do
break 2
done
done
Remember the distinction:
break → leave the loop
continue → skip the current iteration
return → leave the function
exit → terminate the script/shell
Conclusion
The break builtin provides a simple way to stop a Bash loop when its normal termination condition is no longer the right one.
Its most common uses are straightforward:
- stop searching after finding the desired result;
- terminate an interactive loop when the user requests it;
- stop processing after an error;
- leave nested loops with
break 2,break 3, and so on; - exit a
selectmenu.
The basic form is:
break
and for nested loops:
break 2
Use it where it makes the control flow clearer. If a loop has one obvious termination condition, put that condition directly into while, until, or the loop structure itself. When the decision to stop emerges during processing, break is often exactly the right tool.
Sometimes a loop simply needs to know when enough is enough.
That's break.