为啥“继续”在 Foreach-Object 中表现得像“中断”?

Posted

技术标签:

【中文标题】为啥“继续”在 Foreach-Object 中表现得像“中断”?【英文标题】: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 的行为,这样我就不必破坏我的管道?

【问题讨论】:

这是一个页面,其中包含许多与foreach 一起使用的命令:techotopia.com/index.php/… 在这里找到了一个不错的解释和示例...powershell.com/cs/blogs/tips/archive/2015/04/27/… 链接已失效,其他链接并没有真正解释这一点。请问能再找一下吗? 【参考方案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 循环一样好用,而且更直观。

以上是关于为啥“继续”在 Foreach-Object 中表现得像“中断”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Rust RwLock 在 fork 中表现异常?

为啥相同的 MySql 查询在代码和工作台中表现不同?

为啥在具有一级索引的 MultiIndex 列的 pandas DataFrame 中表现不同?

为啥 clojure 的地图在 println 中表现得那样?

如何在 PowerShell 中退出 ForEach-Object

为啥 `additionalProperties` 是在 Swagger/OpenAPI 2.0 中表示 Dictionary/Map 的方式