Powershell 为什么';继续';表现得像';中断';在Foreach对象中?
如果在PowerShell脚本中执行以下操作:Powershell 为什么';继续';表现得像';中断';在Foreach对象中?,powershell,foreach,Powershell,Foreach,如果在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
$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对象
,continue
似乎打破了管道循环
1..100 | ForEach-Object {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
我是否可以在仍对每个对象执行操作时获得类似于
continue
的行为,这样就不必中断管道?因为每个对象的都是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
这是因为continue
应用于外部foreach循环,而不是foreach对象cmdlet。在没有循环的情况下,是最外层的级别,因此给人的印象是它的行为类似于break
那么,您如何获得类似于的行为?其中一种方法当然是:
1..100 | ?{ $_ % 7 -eq 0} | %{Write-Host $_ is a multiple of 7}
另一种选择是一种黑客攻击,但您可以将块封装在一个循环中,该循环将执行一次。这样,继续
将达到预期效果:
1..100 | ForEach-Object {
for ($cont=$true; $cont; $cont=$false) {
if ($_ % 7 -ne 0 ) { continue; }
Write-Host "$($_) is a multiple of 7"
}
}
只需使用返回
而不是继续
。该return
从脚本块返回,该脚本块在特定迭代中由ForEach对象
调用,因此,它模拟循环中的continue
1..100 | ForEach-Object {
if ($_ % 7 -ne 0 ) { return }
Write-Host "$($_) is a multiple of 7"
}
重构时要记住一个问题。有时需要使用foreach对象
cmdlet将foreach
语句块转换为管道(它甚至有别名foreach
,这有助于简化转换,也容易出错)。所有continue
s应替换为return
注:不幸的是,在中为每个对象模拟中断并不是那么容易的
一个简单的否则
语句使其工作如下:
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"}}
使用Where-Object cmdlet是一个很好的建议。在我的实际情况中,我认为将if语句前面的多行代码变成一行难以阅读的代码是没有意义的。但是,在其他情况下,这对我来说是可行的。@justinearing-在我的实际案例中,我认为将if语句前面的多行代码变成一行难以阅读的代码是没有意义的。
你是什么意思?@manojlds也许他认为你的一行解决方案是“难以阅读的”,至少对我来说完全相反。管道方式做事情是非常强大和清晰的,对于这样简单的事情是正确的方法。在shell中编写代码而不利用这一点是毫无意义的。在我的例子中,这是正确的答案,添加一个where条件来过滤掉我将要继续或返回的对象,这样我就不需要首先处理它们+1我非常喜欢Where Object
的建议,因为它允许您在执行其余代码的同时打印备用消息或记录它,这样您就不必在列表中循环多次,而听起来像continue
或return
立即退出循环,因此,如果你想比较两个条件,你就必须建立一个粗糙的组合if
语句块,或者在对象列表中循环几次。坦率地说,这很难看:)而不仅仅是一个hack,因为不是foreach对象,您也可以使用foreach循环。@manojlds:1..100仅用于说明。do{}while($False)对于循环同样有效,而且更直观。下面是一个页面,其中有许多用于foreach
:在这里找到了一个不错的解释和示例。。。链接已经失效,其他链接并不能真正解释这一点。请你再找一遍好吗?根据OP的说法,显然continue
可以用来模拟ForEach对象中的中断)@Richard Hauer这样的continue
将中断整个脚本,而不仅仅是ForEach对象的使用位置。