Why does 'continue' behave like 'break' in a Foreach-Object?
Simply use the return
instead of the continue
. This return
returns from the script block which is invoked by ForEach-Object
on a particular iteration, thus, it simulates the continue
in a loop.
1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) { return } Write-Host "$($_) is a multiple of 7"}
There is a gotcha to be kept in mind when refactoring. Sometimes one wants to convert a foreach
statement block into a pipeline with a ForEach-Object
cmdlet (it even has the alias foreach
that helps to make this conversion easy and make mistakes easy, too). All continue
s should be replaced with return
.
P.S.: Unfortunately, it is not that easy to simulate break
in ForEach-Object
.
Because For-Each
object is a cmdlet and not a loop and continue
and break
do not apply to it.
For example, if you have:
$b = 1,2,3foreach($a in $b) { $a | foreach { if ($_ -eq 2) {continue;} else {Write-Host $_} } Write-Host "after"}
You will get output as:
1after3after
It is because the continue
gets applied to the outer foreach loop and not the foreach-object cmdlet. In absence of a loop, the outermost level, hence giving you an impression of it acting like break
.
So how do you get a continue
-like behaviour? One way is Where-Object of course:
1..100 | ?{ $_ % 7 -eq 0} | %{Write-Host $_ is a multiple of 7}
A simple else
statement makes it work as in:
1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) { # Do nothing } else { Write-Host "$($_) is a multiple of 7" }}
Or in a single pipeline:
1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) {} else {Write-Host "$($_) is a multiple of 7"}}
But a more elegant solution is to invert your test and generate output for only your successes
1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}