Why does 'continue' behave like 'break' in a Foreach-Object?
If I do the following in a PowerShell script:
$range = 1..100
ForEach ($_ in $range) {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
I get the expected output of:
7 is a 开发者_运维百科multiple of 7
14 is a multiple of 7
21 is a multiple of 7
28 is a multiple of 7
35 is a multiple of 7
42 is a multiple of 7
49 is a multiple of 7
56 is a multiple of 7
63 is a multiple of 7
70 is a multiple of 7
77 is a multiple of 7
84 is a multiple of 7
91 is a multiple of 7
98 is a multiple of 7
However, if I use a pipeline and ForEach-Object, continue seems to break out of the pipeline loop.
1..100 | ForEach-Object {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
Can I get a continue-like behavior while still doing ForEach-Object, so I don't have to breakup my pipeline?
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 continues 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,3
foreach($a in $b) {
$a | foreach { if ($_ -eq 2) {continue;} else {Write-Host $_} }
Write-Host "after"
}
You will get output as:
1
after
3
after
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"}}
Another alternative is kind of a hack, but you can wrap your block in a loop that will execute once. That way, continue will have the desired effect:
1..100 | ForEach-Object {
for ($cont=$true; $cont; $cont=$false) {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
}
加载中,请稍侯......
精彩评论