Loop control statements allow you to alter the execution flow of loops based on runtime conditions. The break and continue statements change the normal flow of a loop: break exits the loop entirely, while continue skips the remainder of the current iteration and moves on to the next one. In complex scripts with nested loops, labeled loops extend this control to target a specific outer loop rather than just the innermost one.
The break Statement
The break statement immediately halts execution of the innermost enclosing loop (for, foreach, while, do-while, do-until) or switch block. Once break is encountered, the PowerShell engine exits the loop and resumes execution at the first statement following the loop's closing curly brace (}).
Syntax and Basic Operation
$Numbers = 1..10
foreach ($num in $Numbers) {
if ($num -eq 4) {
Write-Output "Target value 4 reached. Executing break."
break
}
Write-Output "Processing number: $num"
}
Write-Output "Loop exited. Execution resumed."where:
foreach ($num in $Numbers)iterates through numbers 1 to 10.if ($num -eq 4)tests for the termination condition.breakstops the loop immediately when$numreaches4.Write-Output "Loop exited..."runs after the loop terminates.
Execute the script from the console:
./break-basic.ps1Processing number: 1
Processing number: 2
Processing number: 3
Target value 4 reached. Executing break.
Loop exited. Execution resumed.Another example:
foreach ($number in 1..10) {
if ($number -eq 5) {
Write-Output "Reached 5, stopping the loop."
break
}
Write-Output "Number: $number"
}where once $number equals 5, break exits the foreach loop entirely: numbers 6 through 10 are never processed.
Execute the script from the console:
./break-basic.ps1Number: 1
Number: 2
Number: 3
Number: 4
Reached 5, stopping the loop.The continue Statement
The continue statement terminates execution of the current iteration only. When continue is reached, PowerShell skips any remaining statements in the loop body for that iteration and returns control to the top of the loop:
- In a
forloop, control jumps to the<Repeat>expression (e.g.,$i++), followed by condition re-evaluation. - In a
whileordo-whileloop, control jumps directly to condition re-evaluation. - In a
foreachloop, control advances immediately to the next element in the collection.
Syntax and Basic Operation
$Services = @("wuauserv", "XboxNetApiSvc", "Spooler", "XblAuthManager")
foreach ($service in $Services) {
if ($service -like "Xb*") {
Write-Warning "Skipping non-essential service: $service"
continue
}
Write-Output "Auditing required system service: $service"
}where:
if ($service -like "Xb*")identifies items to ignore using a wildcard match.continueskips the remaining lines of the loop body for matching items and immediately begins the next iteration.
foreach ($number in 1..10) {
if ($number % 2 -eq 0) {
continue
}
Write-Output "Odd number: $number"
}where:
$number % 2 -eq 0checks whether the number is even.continueskips theWrite-Outputline for even numbers, but the loop keeps running.
Execute the script from the console:
./continue-basic.ps1Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9pwsh ./continue-demo.ps1Auditing required system service: wuauserv
WARNING: Skipping non-essential service: XboxNetApiSvc
Auditing required system service: Spooler
WARNING: Skipping non-essential service: XblAuthManagerLabeled Loops
When loops are nested (a loop inside another loop), a standard break or continue statement only acts upon the innermost loop that directly encloses it by default. A label, written as :labelName before the outer loop, lets you target that specific loop directly from inside a nested block.
Syntax
:LabelName foreach ($item in $collection) {
while ($condition) {
break LabelName # Breaks out of the outer labeled loop
continue LabelName # Advances the outer labeled loop to its next pass
}
}:outerLoop foreach ($row in 1..3) {
foreach ($col in 1..3) {
if ($col -eq 2 -and $row -eq 2) {
Write-Output "Breaking outer loop at row=$row, col=$col"
break outerLoop
}
Write-Output "Row: $row, Col: $col"
}
}where:
:outerLooplabels the outerforeachloop.break outerLoopexits both loops entirely, not just the inner one, as soon as the condition is met.
./labeled-loop.ps1Row: 1, Col: 1
Row: 1, Col: 2
Row: 1, Col: 3
Row: 2, Col: 1
Breaking outer loop at row=2, col=2Labeled continue
continue can also target a labeled outer loop, skipping directly to its next iteration rather than the inner loop's next iteration.
:outerLoop foreach ($row in 1..3) {
foreach ($col in 1..3) {
if ($col -eq 2) {
continue outerLoop
}
Write-Output "Row: $row, Col: $col"
}
}where when $col reaches 2, continue outerLoop skips straight to the next row, bypassing the rest of the inner loop's columns entirely.
./labeled-continue.ps1Row: 1, Col: 1
Row: 2, Col: 1
Row: 3, Col: 1Labeled Loop Search Example
$Datacenters = @("DC-East", "DC-West", "DC-Central")
$TargetServer = "sql-node-02"
:DatacenterScan foreach ($dc in $Datacenters) {
Write-Output "Entering region: $dc"
$Nodes = @("web-node-01", "sql-node-02", "app-node-03")
foreach ($node in $Nodes) {
Write-Output " Checking node: $node"
if ($node -eq $TargetServer) {
Write-Output " >>> Target $TargetServer located in $dc! Halting all scans."
break DatacenterScan
}
}
}
Write-Output "Search operation finalized."where:
:DatacenterScandefines a label for the outerforeachloop.foreach ($node in $Nodes)represents the inner loop searching servers within the active region.break DatacenterScanbreaks out of the outer loop entirely as soon as$TargetServeris matched, bypassing both the remaining nodes inDC-Eastand the subsequent datacenters (DC-West,DC-Central).
pwsh ./labeled-loop-search.ps1Entering region: DC-East
Checking node: web-node-01
Checking node: sql-node-02
>>> Target sql-node-02 located in DC-East! Halting all scans.
Search operation finalized.Practical Example: Searching a Nested Collection
Labeled loops are especially useful when searching through nested data and needing to stop all iteration as soon as a match is found.
$grid = @(
@("A1", "A2", "A3"),
@("B1", "B2", "B3"),
@("C1", "C2", "C3")
)
$target = "B2"
$found = $false
:searchLoop for ($row = 0; $row -lt $grid.Count; $row++) {
for ($col = 0; $col -lt $grid[$row].Count; $col++) {
if ($grid[$row][$col] -eq $target) {
Write-Output "Found '$target' at row $row, column $col"
$found = $true
break searchLoop
}
}
}
if (-not $found) {
Write-Output "'$target' was not found in the grid."
}./find-in-grid.ps1Found 'B2' at row 1, column 1Practical Example: Multi-Tier Infrastructure Health Inspection
The following script performs a multi-tier infrastructure scan across server environments and services. It uses continue to skip non-critical or maintenance-mode hosts, and a labeled break to immediately halt deployment across all environments if a critical tier-1 failure occurs:
$Environments = [ordered]@{
"Staging" = @("stg-web-01", "stg-maint-01", "stg-db-01")
"Production" = @("prd-web-01", "prd-db-01")
}
$AuditLog = [System.Collections.Generic.List[PSCustomObject]]::new()
:EnvLoop foreach ($envKey in $Environments.Keys) {
Write-Output "Auditing Environment: $envKey"
$HostList = $Environments[$envKey]
:HostLoop foreach ($hostname in $HostList) {
if ($hostname -like "*-maint-*") {
Write-Warning "Skipping maintenance node: $hostname"
continue HostLoop
}
$isOnline = ($hostname -ne "stg-db-01")
$AuditLog.Add([PSCustomObject]@{
Environment = $envKey
NodeName = $hostname
Status = if ($isOnline) { "HEALTHY" } else { "CRITICAL_OFFLINE" }
Timestamp = (Get-Date -Format "HH:mm:ss")
})
if (!$isOnline -and $hostname -like "*-db-*") {
Write-Error "CRITICAL: Database offline on $hostname! Aborting audit across all tiers."
break EnvLoop
}
}
}
Write-Output "`n=== Audit Summary ==="
$AuditLog | Format-Table -AutoSizewhere:
:EnvLoopand:HostLoopprovide labels for the nested loops.if ($hostname -like "*-maint-*") continue HostLoopskips nodes in maintenance mode and advances to the next host.if (!$isOnline -and $hostname -like "*-db-*") break EnvLoopimmediately halts all inspection across all environments when an offline database is detected.[System.Collections.Generic.List[PSCustomObject]]::new()provides an in-memory collection to record results.
Execute the script from the console:
pwsh ./audit-infrastructure-tiers.ps1Auditing Environment: Staging
WARNING: Skipping maintenance node: stg-maint-01
Write-Error: CRITICAL: Database offline on stg-db-01! Aborting audit across all tiers.
=== Audit Summary ===
Environment NodeName Status Timestamp
----------- -------- ------ ---------
Staging stg-web-01 HEALTHY 20:52:13
Staging stg-db-01 CRITICAL_OFFLINE 20:52:13