Why does continue behave like break in a Foreach-O

2019-01-05 02:43发布

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"
}

My questions is, can I get a continue like behavior while still doing ForEach-Object, so I don't have to breakup my pipeline?

4条回答
乱世女痞
2楼-- · 2019-01-05 02:47

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"
        }
}
查看更多
Summer. ? 凉城
3楼-- · 2019-01-05 02:57

A simple else statement makes it work as is

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"}}
查看更多
乱世女痞
4楼-- · 2019-01-05 02:59

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"
}

This is a gotcha to be kept in mind on 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 should be replaced with return.

P.S. Unfortunately, it is not that easy to simulate break in ForEach-Object.

查看更多
一夜七次
5楼-- · 2019-01-05 03:08

Because For-Each object is a cmdlet and not a loop and continue / 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, outermost level, hence giving you an impression of it acting like break.

So how do you get continue like behaviour? One way is where-object ofcourse:

1..100 | ?{ $_ % 7  -eq 0} | %{write-host $_ is a mutliple of 7}
查看更多
登录 后发表回答