Skip to content
shellmap

breakBreak out of a loop in bash, zsh, fish, PowerShell, and cmd across all 5 shells

Equivalents in every shell

Bashunix
for i in 1 2 3; do [ "$i" = "2" ] && break; echo "$i"; done

bash builtin. `break N` (N ≥ 1) breaks out of N nested loops — `break 2` exits two levels up. Works in `for`, `while`, `until`, `select`. From a function called inside a loop, `break` works as if invoked directly inside the loop.

Zshunix
for i in 1 2 3; do [[ $i = 2 ]] && break; echo $i; done

zsh builtin. Same `break N` semantics as bash. zsh's extended `[[ ... ]]` is preferred over POSIX `[ ... ]`. zsh `repeat 5 { … }` loops also accept `break`.

Fishunix
for i in 1 2 3; if test $i = 2; break; end; echo $i; end

fish builtin. Fish does NOT support `break N` (multi-level) — only single-level break. To exit multiple nested loops, set a flag variable in the inner loop and `break` from each level: `for i in ...; for j in ...; if test $cond; set done 1; break; end; end; if test "$done" = "1"; break; end; end`.

PowerShellwindows
foreach ($i in 1..3) { if ($i -eq 2) { break }; $i }

pwsh keyword. Works in `for` / `foreach` / `while` / `do…while` / `switch` (yes — `break` exits a switch case). pwsh supports LABELED loops: `:outer foreach ($x in ...) { foreach ($y in ...) { break outer } }` exits both. The label syntax is unique to pwsh among the major shells.

cmd.exewindows
for %i in (1 2 3) do (if "%i"=="2" goto :done & echo %i) :done

cmd has NO `break` keyword for `for` loops. The workaround is `goto :label` — set a label after the loop and `goto :label` from inside the loop body. Verbose, but it's the only way. For `goto :EOF` exits the entire batch file (similar to `exit /b`).

Worked examples

Break out of the inner loop when a condition is met

Bash
for f in *.log; do grep -q ERROR "$f" && { echo "$f"; break; }; done
PowerShell
foreach ($f in Get-ChildItem *.log) { if (Select-String -Path $f -Pattern "ERROR" -Quiet) { $f.Name; break } }

Break out of TWO nested loops (multi-level)

Bash
for i in 1 2 3; do for j in a b c; do [ "$j" = "b" ] && break 2; done; done
PowerShell
:outer foreach ($i in 1..3) { foreach ($j in "a","b","c") { if ($j -eq "b") { break outer } } }
Fish
set done 0
for i in 1 2 3
  for j in a b c
    if test $j = b; set done 1; break; end
  end
  if test $done -eq 1; break; end
end

Break a `while true` loop on a signal-equivalent condition

Bash
while true; do read -t 5 line || break; echo "$line"; done
PowerShell
while ($true) { $line = Read-Host -Timeout 5 -ErrorAction SilentlyContinue; if (-not $line) { break }; $line }

Gotchas

  • fish has NO multi-level `break N`. The flag-variable workaround is the idiom. This is the single biggest porting trap when moving bash multi-level-break code to fish.
  • In pwsh `switch` statements, the implicit fall-through behavior means EVERY case can match. `break` is needed inside each case to prevent execution of subsequent cases (or set the `default` case last). This differs from C/Java where `switch` cases naturally fall through unless broken — pwsh switch behaves like fall-through but executes ALL matching cases by default.
  • cmd `for` loops have no `break` equivalent. The `goto :label` workaround works, but `goto` inside parenthesized blocks (compound statements) has known issues — the loop variable goes out of scope. Use `call :subroutine` patterns instead, or rewrite as a `for /f` over filtered input that pre-applies the break condition.
  • bash `break N` where N exceeds the number of enclosing loops silently breaks ALL loops (no error). `break 99` in a single loop just exits that one loop. Misuse is hard to detect — always count nesting explicitly.
  • In bash, `break` inside a pipeline-with-subshell `echo X | while read line; do break; done; cmd2` breaks the subshell's loop, not the parent — useful subtlety. Some loops accidentally run in a subshell due to piping and `break` from outside the pipeline won't affect them.

WSL & PowerShell Core notes

pwshpwsh `break` is identical across Windows / Linux / macOS. Labeled-break (`:label`) is unique among shells — use it for multi-level loop exits in pwsh scripts since you don't need flag-variable workarounds.
WSLWSL bash `break` works as on native Linux. Cross-shell loops via `for f in (cmd.exe /c dir /b)` (or `for $f in $(wsl.exe ls)` from cmd) inherit the originating shell's break semantics.

Related commands