Share via


PowerShell Gotcha -- ForEach-Object and "Continue"

I came across an interesting behavior when looping with the ForEach-Object cmdlet that took some thought to figure out why the behavior was occurring.  I was doing some bitwise anding on security descriptor and ACE flags when I realized that the output that I was expecting was noticably short.  After manually walking the process, I was able to identify that one of my loops using the ForEach-Object cmdlet was behaving very oddly and I had to figure out why.

Without going into too much detail, consider the over-simplified example below as a demonstration of the problem:

$a = 1..10

foreach ($i in $a) {
    if ($i % 2 -eq 0) {
        $i
    } else {
        continue
    }
}

$a | ForEach-Object {
    if ($_ % 2 -eq 0) {
        $_
    } else {
        continue
    }
}

...notice that only one set of multiples of 2 printed out.  What happens in the ForEach-Object loop is that the first number passed into the loop is a 1 and when divided by 2, has a remainder so the ELSE condition applies in the first iteration.  The 'continue' keyword is executed and processing terminates... but why?  Well, ForEach-Object is a CMDLET so when a continue keyword is encountered, the processing scope is the script so processing of the script terminates.  To demonstrate, take another over-simplified example:

Write-Host "Print me to the screen please!"
continue
Write-Host "Print me too please!"

... and only the first phrase prints to the screen.  This is because the processing scope is still the script when the continue keyword is encountered so script processing terminates.  So I can hear you saying, "great, but how do I fix it?"  Well, that's easy -- When using the ForEach-Object cmdlet and you want to emulate the continue keyword behavior in traditional loops, simply use the RETURN keyword instead.  So your loop becomes:

  $a | ForEach-Object {
    if ($_ % 2 -eq 0) {
        $_
    } else {
        return
    }
}

and now behaves as expected and intended.