【问题标题】:Why does 'continue' behave like 'break' in a Foreach-Object?为什么“继续”在 Foreach-Object 中表现得像“中断”?
【发布时间】:2011-12-07 06:47:54
【问题描述】:

如果我在 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"
}

我能否在仍然执行 ForEach-Object 的同时获得类似 continue 的行为,这样我就不必破坏我的管道?

【问题讨论】:

标签: powershell foreach


【解决方案1】:

只需使用return 而不是continue。这个return 从脚本块返回,该脚本块由ForEach-Object 在特定迭代中调用,因此,它在循环中模拟continue

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

重构时需要牢记一个问题。有时人们想将foreach 语句块转换为带有ForEach-Object cmdlet 的管道(它甚至具有别名foreach,这有助于使这种转换变得容易并且也容易出错)。所有continues 都应替换为return

P.S.:不幸的是,在ForEach-Object 中模拟break 并不容易。

【讨论】:

  • 从OP所说的显然continue可以用来模拟break中的ForEach-Object:)
  • @Richard Hauer 这样的continue 会破坏整个脚本,而不仅仅是使用它的ForEach-Object
【解决方案2】:

因为For-Each 对象是一个cmdlet 而不是循环,并且continuebreak 不适用于它。

例如,如果您有:

$b = 1,2,3

foreach($a in $b) {

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

    Write-Host  "after"
}

你会得到如下输出:

1
after
3
after

这是因为 continue 被应用于外部 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 也许他认为您的单行解决方案“难以阅读”,至少对我来说完全相反。做事的管道方式非常强大和清晰,是处理简单事情的正确方法。在 shell 中编写代码而不利用它是没有意义的。
  • 在我的情况下这是正确的答案,添加一个 where 条件来过滤掉我将继续或返回的对象,这样我就不需要首先处理它们. +1
  • 我真的很喜欢Where-Object 的建议,因为它允许您在执行其余代码的同时打印替代消息或记录它,这样您就不必多次循环遍历列表, 虽然听起来像 continuereturn 立即退出循环,所以如果你有几个条件想要比较,你将不得不建立一个粗糙的组合 if 语句块或循环几次不同的对象列表。
【解决方案3】:

一个简单的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"}}

【讨论】:

    【解决方案4】:

    另一种选择是一种 hack,但您可以将块包装在一个循环中,该循环将执行一次。这样,continue 就会达到预期的效果:

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

    【讨论】:

    • 坦率地说,这很丑 :) 而且不仅仅是一个 hack,因为你可以使用 foreach 循环而不是 foreach 对象。
    • @manojlds: 1..100 仅用于说明。 do {} while($False) 和 for 循环一样好用,而且更直观。
    猜你喜欢
    • 2012-08-12
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 2011-12-11
    相关资源
    最近更新 更多