Skip to content

PowerShell Break and Continue in Loops

How to use PowerShell's break and continue statements to control loop execution, including handling nested loops with labeled breaks and continues for efficient script termination.

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

break-demo.ps1powershell
$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.
  • break stops the loop immediately when $num reaches 4.
  • Write-Output "Loop exited..." runs after the loop terminates.

Execute the script from the console:

Consolepowershell
./break-basic.ps1
Outputplaintext
Processing number: 1
Processing number: 2
Processing number: 3
Target value 4 reached. Executing break.
Loop exited. Execution resumed.

Another example:

break-basic.ps1powershell
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:

Consolepowershell
./break-basic.ps1
Outputplaintext
Number: 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 for loop, control jumps to the <Repeat> expression (e.g., $i++), followed by condition re-evaluation.
  • In a while or do-while loop, control jumps directly to condition re-evaluation.
  • In a foreach loop, control advances immediately to the next element in the collection.

Syntax and Basic Operation

continue-demo.ps1powershell
$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.
  • continue skips the remaining lines of the loop body for matching items and immediately begins the next iteration.
continue-basic.ps1powershell
foreach ($number in 1..10) {
    if ($number % 2 -eq 0) {
        continue
    }
    Write-Output "Odd number: $number"
}

where:

  • $number % 2 -eq 0 checks whether the number is even.
  • continue skips the Write-Output line for even numbers, but the loop keeps running.

Execute the script from the console:

Consolepowershell
./continue-basic.ps1
Outputplaintext
Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9
Consolepowershell
pwsh ./continue-demo.ps1
Outputplaintext
Auditing required system service: wuauserv
WARNING: Skipping non-essential service: XboxNetApiSvc
Auditing required system service: Spooler
WARNING: Skipping non-essential service: XblAuthManager

Labeled 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
    }
}
labeled-loop.ps1powershell
: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:

  • :outerLoop labels the outer foreach loop.
  • break outerLoop exits both loops entirely, not just the inner one, as soon as the condition is met.
Consolepowershell
./labeled-loop.ps1
Outputplaintext
Row: 1, Col: 1
Row: 1, Col: 2
Row: 1, Col: 3
Row: 2, Col: 1
Breaking outer loop at row=2, col=2

Labeled continue

continue can also target a labeled outer loop, skipping directly to its next iteration rather than the inner loop's next iteration.

labeled-continue.ps1powershell
: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.

Consolepowershell
./labeled-continue.ps1
Outputplaintext
Row: 1, Col: 1
Row: 2, Col: 1
Row: 3, Col: 1

Labeled Loop Search Example

labeled-loop-search.ps1powershell
$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:

  • :DatacenterScan defines a label for the outer foreach loop.
  • foreach ($node in $Nodes) represents the inner loop searching servers within the active region.
  • break DatacenterScan breaks out of the outer loop entirely as soon as $TargetServer is matched, bypassing both the remaining nodes in DC-East and the subsequent datacenters (DC-West, DC-Central).
Consolepowershell
pwsh ./labeled-loop-search.ps1
Outputplaintext
Entering 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.

find-in-grid.ps1powershell
$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."
}
Consolepowershell
./find-in-grid.ps1
Outputplaintext
Found 'B2' at row 1, column 1

Practical 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:

audit-infrastructure-tiers.ps1powershell
$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 -AutoSize

where:

  • :EnvLoop and :HostLoop provide labels for the nested loops.
  • if ($hostname -like "*-maint-*") continue HostLoop skips nodes in maintenance mode and advances to the next host.
  • if (!$isOnline -and $hostname -like "*-db-*") break EnvLoop immediately 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:

Consolepowershell
pwsh ./audit-infrastructure-tiers.ps1
Outputplaintext
Auditing 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
Navigation