KlaymenDK
KlaymenDK

Reputation: 724

Why does my break statement break "too far"?

Here's a snippet of my code (based on this previous question):

$rgdNames = (Get-AzureRmResourceGroupDeployment -ResourceGroupName "$azureResGrpName").DeploymentName
$siblings = $rgdNames | Where-Object{$_ -match "^($hostname)(\d+)$" }
if ($siblings) {
    # Make a list of all numbers in the list of hostnames
    $serials = @()
    foreach ($sibling in $siblings) {
        # $sibling -split ($sibling -split '\d+$') < split all digits from end, then strip off everything at the front
        # Then convert it to a number and add that to $serials
        $hostnumber = [convert]::ToInt32([string]$($sibling -split ($sibling -split '\d+$'))[1], 10)
        $serials += $hostnumber
    }
    (1..$siblingsMax) | foreach { # Iterate over all valid serial numbers
        if (!$serials.Contains($_)) { # Stop when we find a serial number that isn't in the list of existing hosts
            $serial = $_
             # break # FIXME: For some reason, this break statement breaks "too far"
        }
    }
} else {
    $serial = 1
}
write-output("serial = $serial") # does not print
# ...more statements here, but they're never called :(

I have been looking at it for a while, but can't figure out why the break statement (if un-commented) stops my program instead of just breaking out of its foreach loop. Is there something about foreach that I'm not aware of, or does break just work differently than it would in Java?

For the time being, I'm working around this using an extra if test, and it doesn't mean (too) much that the loop runs its entire length. But it's ugly!

Upvotes: 0

Views: 84

Answers (1)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174435

This construct:

(1..$siblingsMax) | foreach { # Iterate over all valid serial numbers
    # do stuff
}

is NOT a foreach loop - it's a pipeline with a call to the ForEach-Object cmdlet (aliased as foreach) - and keywords designed to interrupt the control flow of a loop (like break or continue) will NOT act in the same way in these two different cases.

Use a proper foreach loop and break will behave as you expect:

foreach($i in 1..$siblingsMax){
    # do stuff
    if($shouldBreak)
    {
        break
    }
}

Alternatively, you can abuse the fact that continue behaves like you would expect break in a ForEach-Object process block:

(1..$siblingsMax) | foreach { # Iterate over all valid serial numbers
    if($shouldBreak)
    {
        continue
    }
}

although I would strongly discourage this approach (it'll only lead to more confusion)

Upvotes: 1

Related Questions