| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user |
| Scope | Bash break and continue in for, while, until, and select, including numeric levels (break 2, continue 2) and how they differ from return and exit. Does not cover loop syntax from scratch or full select menu design. |
| Related guides | Bash shell script tutorial Shell scripting course hub |
Bash break exits a loop early, while continue skips the rest of the current iteration and moves to the next one. Both also accept a numeric level for controlling nested loops.
After break, execution resumes on the first command after done. After continue, Bash starts the next loop iteration and skips whatever was left in the body.
break vs continue — Quick Answer
Both commands apply to the innermost enclosing loop unless you pass a numeric level.
| Command | What it does | Execution resumes |
|---|---|---|
break |
Exits the loop | First line after the matching done |
continue |
Skips the rest of this iteration | Next iteration of the same loop |
break N |
Exits N nested loop levels |
First line after the Nth enclosing done |
continue N |
Skips the rest of the current iteration up through N nesting levels |
At the next iteration of the Nth enclosing loop |
The optional number counts loop nesting levels, not how many iterations to skip. break 2 does not mean "break after two passes"; it means "leave the current loop and the loop that wrapped it."
Syntax for both builtins:
break [n]
continue [n]n is optional and must be 1 or greater. Without it, Bash targets the innermost loop. break 1 is equivalent to break, and continue 1 is equivalent to continue. If n is greater than the number of active enclosing loops, Bash exits or continues as many enclosing loop levels as are available. A non-positive count such as break 0 is an error (break: 0: loop count out of range).
Exit a Loop with break
Use break when you found what you were searching for and later iterations would waste time.
Create two small log files, then stop at the first file that contains ERROR:
printf 'INFO startup\nERROR failed\n' > file1.log && printf 'INFO healthy\n' > file2.logRun the scan over the fixed list:
for f in file1.log file2.log; do
echo "checking $f"
if grep -q 'ERROR' "$f"; then
echo "match in $f"
break
fi
donechecking file1.log
match in file1.logfile2.log is never checked because break left the for loop as soon as file1.log matched. Execution continues on the line after done.
The same pattern works when you read a file line by line and stop at a sentinel or first hit. Build search.txt, then read it line by line and stop at the first line whose prefix is found plus a colon:
key=found
colon=:
printf '%s\n' 'no match here' "${key}${colon}alpha" 'tail' > search.txt
while IFS= read -r line; do
echo "scan: $line"
if [ "${line#${key}${colon}}" != "$line" ]; then
echo "first match"
break
fi
done < search.txtscan: no match here
scan: found:alpha
first matchThe tail line is never scanned because the loop ended at the first matching prefix.
Prefer input redirection (done < file) over piping into while read when loop state matters. A loop on the right side of a pipeline normally runs in a subshell in Bash, so break exits that subshell rather than changing control flow in the parent shell. When the input comes from a command, use process substitution instead:
while IFS= read -r line; do
...
done < <(producer)Replace producer with the command that prints the lines you want to read.
Skip an Iteration with continue
Use continue when one item in the list is invalid or irrelevant but the rest still need processing.
Skip one filename in a fixed list and process the others:
for f in app.log skip.log error.log; do
if [[ "$f" == skip.log ]]; then
echo "skip: $f"
continue
fi
echo "process $f"
doneprocess app.log
skip: skip.log
process error.logcontinue jumps to the next list item without running echo "process $f" for skip.log. The loop itself keeps going.
Use break and continue in while and until
break and continue work the same in while and until loops; only the loop test differs.
Exit a while loop when a counter reaches a limit. Numeric tests such as (( n < 5 )) use the Bash arithmetic command form:
n=0
while (( n < 5 )); do
((n++))
echo "while n=$n"
if (( n == 3 )); then
echo 'break'
break
fi
done
echo 'after while'while n=1
while n=2
while n=3
break
after whilen never reaches 4 or 5 because break ran inside the body.
In an until loop, continue skips the remaining commands in the body, then Bash evaluates the until condition for the next iteration:
n=0
until (( n >= 5 )); do
((n++))
echo "until n=$n"
if (( n == 2 )); then
echo 'continue'
continue
fi
echo "body n=$n"
doneuntil n=1
body n=1
until n=2
continue
until n=3
body n=3
until n=4
body n=4
until n=5
body n=5Here n has already been incremented before continue runs. When n is 2, the echo "body n=$n" line never runs, but the next pass still starts with ((n++)) at the top of the body.
Nested Loops with break 2
In nested loops, a plain break only leaves the inner loop. Use break 2 when a condition in the inner loop should end both levels.
If the host-and-port example below used plain break, only the port loop would stop; after inner ($host) would still run and the outer host loop would continue.
Host and port scan: stop entirely when db:80 appears:
for host in web db; do
for port in 22 80 443; do
echo "try $host:$port"
if [[ "$host" == db && "$port" == 80 ]]; then
echo 'break 2'
break 2
fi
done
echo "after inner ($host)"
done
echo 'script continues'try web:22
try web:80
try web:443
after inner (web)
try db:22
try db:80
break 2
script continuesbreak 2 skipped try db:443, skipped after inner (db), and landed on script continues. The outer loop did not start another host.
Nested Loops with continue 2
continue 2 skips the rest of the inner loop and the remainder of the current outer iteration, then starts the outer loop's next item.
Skip every port 80 and move straight to the next host:
for host in web db; do
for port in 22 80 443; do
echo "try $host:$port"
if [[ "$port" == 80 ]]; then
echo 'continue 2'
continue 2
fi
echo "inner tail $host:$port"
done
echo "after inner ($host)"
donetry web:22
inner tail web:22
try web:80
continue 2
try db:22
inner tail db:22
try db:80
continue 2Port 443 never runs for either host. after inner never prints because continue 2 jumped to the next value of host before the inner loop finished.
break in a Bash select Menu
A select loop runs until you break out of it. That is the usual way to close an interactive menu.
select item in Alpha Beta Quit; do
case "$item" in
Quit)
break
;;
*)
echo "picked $item"
;;
esac
done
echo 'menu closed'The loop is interactive. A short session might look like this:
1) Alpha
2) Beta
3) Quit
#? 1
picked Alpha
#? 3
menu closedAfter Quit, break ends the loop and menu closed prints. Without break, select would prompt again.
break vs return vs exit
These commands affect different scopes:
| Command | Scope | Effect |
|---|---|---|
break |
Innermost loop (or N levels) |
Leaves loop; caller or script continues |
continue |
Innermost loop (or N levels) |
Next loop iteration |
return |
Current shell function | Exits the function; script continues |
exit |
Entire script or shell | Ends the process with an exit status |
break does not exit a function or script. return inside a loop leaves the whole function, not just the loop:
function demo_return {
for i in 1 2 3; do
if [[ $i -eq 2 ]]; then
return
fi
echo "i=$i"
done
echo 'after loop in demo_return'
}
demo_return
echo 'caller continues'i=1
caller continuesAt i=2, return ended demo_return before i=3 or after loop in demo_return ran. Use break when you only want to leave the loop inside a function; see Bash functions for how return ends the whole function.
Practical Parsing Example
Real scripts combine continue for bad records and break for a stop sentinel. Create data.txt with mixed lines and an END marker:
printf '%s\n' 'ok:10' 'bad line' 'ok:20' 'skip:30' 'ok:40' 'END' > data.txtParse lines that start with ok:, skip anything else, and stop at END. A prefix test with ${line#ok:} checks whether stripping ok: changes the line:
while IFS= read -r line; do
[[ "$line" == END ]] && break
if [ "${line#ok:}" != "$line" ]; then
echo "parsed ${line#ok:}"
else
echo "skip: $line"
continue
fi
done < data.txtparsed 10
skip: bad line
parsed 20
skip: skip:30
parsed 40bad line and skip:30 did not start with ok:, so continue moved on. The END line triggered break before any fifth value printed. For larger files, see read a file line by line in Bash for IFS and read details.
Common Mistakes
| Symptom | Likely cause | Fix |
|---|---|---|
| Script exits when you wanted to leave one loop | Used exit instead of break |
break stays inside the script; exit ends it |
Outer loop keeps running after inner break |
Forgot nested levels | Use break 2 (or higher) to leave multiple loops |
break: 0: loop count out of range |
Used 0 or another invalid level |
Use a positive integer such as break, break 1, or break 2 |
break / continue reports it is only meaningful in a loop |
Command is outside an active loop | Use it inside for, while, until, or select; use return for a function or exit for the script |
| Function ends but you only meant to stop the loop | Used return where break fits |
break for loop only; return for whole function |
Code after continue still confuses readers |
Dead code below continue |
Put skip logic in if / case; only reachable work below |
Deep nesting with many numeric break levels is hard to follow. When a script needs that often, extract the inner loop into a function and use return.
Summary
Bash break stops a loop and hands control to the first command after done. continue skips the rest of the current iteration and starts the next iteration according to that loop's normal control flow. Both accept an optional level: break 2 and continue 2 target outer loops by nesting depth, not by iteration count.
You used break to stop at the first log match and the first found: line, and continue to skip one list item or a malformed record. Nested host-and-port examples showed where execution lands after break 2 versus continue 2. In a select menu, break is the normal way to quit.
The main mix-up is reaching for exit or return when only the loop should change. break and continue affect loops; return leaves a function; exit ends the shell. For line-oriented input, prefer done < file or process substitution over piping into while read, and pair continue for bad lines with break for a sentinel such as END.
References
Frequently Asked Questions
What does break 2 mean?
It exits two nested loop levels at once. Bash counts enclosing loops from the inside out; break 2 leaves the inner loop and the loop that wrapped it, then continues after the outer loop's done.
Can continue target an outer loop?
Yes. continue 2 skips the rest of the inner loop and the remainder of the current outer iteration, then starts the outer loop's next pass. The number is nesting depth, not how many iterations to skip.
Does break exit the script?
No. break only leaves the loop. Commands after done still run. Use exit to end the entire script or shell session.
Can I use break in select?
Yes. break is the standard way to leave a select loop, for example when the user picks Quit. Without break, select keeps prompting.

