为什么在Foreach对象中“继续”的行为类似于“中断”?


123

如果我在PowerShell脚本中执行以下操作:

$range = 1..100
ForEach ($_ in $range) {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

我得到了预期的输出:

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

但是,如果我使用管道和ForEach-Objectcontinue似乎会中断管道循环。

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

continue在仍然执行ForEach-Object的同时还能获得类似的行为,所以我不必中断管道?


这是一个包含许多命令的页面foreachtechotopia.com/index.php/…–
bgmCoder

找到一份像样的解释和样品这里... powershell.com/cs/blogs/tips/archive/2015/04/27/...
弥敦道哈特利

Answers:


164

只需使用return代替即可continue。这return从在ForEach-Object特定迭代中调用的脚本块返回,因此,它continue在循环中模拟。

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { return }
    Write-Host "$($_) is a multiple of 7"
}

重构时要牢记一些陷阱。有时,有人想将一个foreach语句块转换为带有ForEach-Objectcmdlet 的管道(它甚至具有别名foreach,这也有助于简化此转换并简化错误)。所有continue都应替换为return

PS:遗憾的是,它不是那么容易模仿breakForEach-Object


2
从OP的说法来看,显然continue可以用来模拟breakin ForEach-Object:)
Richard Hauer

6
@ Richard Hauer这样的a continue会破坏整个脚本,而不仅仅是ForEach-Object使用它。
Roman Kuzmin 2015年

22

因为For-Eachobject是cmdlet而不是循环,continue并且break不适用于它。

例如,如果您有:

$b = 1,2,3

foreach($a in $b) {

    $a | foreach { if ($_ -eq 2) {continue;} else {Write-Host $_} }

    Write-Host  "after"
}

您将获得以下输出:

1
after
3
after

这是因为将continuegets应用于外部foreach循环,而不是foreach-object cmdlet。如果没有循环,则是最外面的级别,因此给您印象深刻break

那么,您如何获得类似continue的行为?一种方法是Where-Object当然:

1..100 | ?{ $_ % 7  -eq 0} | %{Write-Host $_ is a multiple of 7}

使用Where-Object cmdlet是一个很好的建议。在我的实际情况中,我认为将if语句之前的多行代码变成难读的一行代码是没有道理的。但是,在其他情况下,这对我有用。
贾斯汀·迪林

@JustinDearing- In my actual case, I don't think it makes sense to make the multiple lines of code preceding my if statement into a single long line of hard to read code.你是什​​么意思?
manojlds 2011年

3
@manojlds也许他认为您的单行解决方案“很难阅读”,至少对我而言完全相反。做事情的流水线方法确实强大而清晰,是处理类似简单事情的正确方法。在shell中编写代码而不利用它是没有意义的。
mjsr 2011年

在我的情况下,这是正确的答案,添加一个where条件以过滤掉我将要继续执行或返回的对象,这样我就不需要首先处理它们。+1
克里斯·马格努森

3

另一种选择是一种破解,但是您可以将块包装在一个循环中,该循环将执行一次。这样,continue将具有预期的效果:

1..100 | ForEach-Object {
    for ($cont=$true; $cont; $cont=$false) {
        if ($_ % 7 -ne 0 ) { continue; }
        Write-Host "$($_) is a multiple of 7"
    }
}

4
坦白说,这很丑陋:)不仅是hack,因为您可以使用foreach循环来代替foreach对象。
manojlds 2011年

1
@manojlds:1..100仅用于说明。做{} while($ False)和for循环一样好用,并且更加直观。
哈里·马蒂罗斯

2

一个简单的else语句使其工作方式如下:

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) {
        # Do nothing
    } else {
        Write-Host "$($_) is a multiple of 7"
    }
}

或在单个管道中:

1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) {} else {Write-Host "$($_) is a multiple of 7"}}

但是,更优雅的解决方案是反转测试并仅为成功生成输出

1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.