web-dev-qa-db-ja.com

なぜForeach-Objectのブレークのように動作し続けるのですか?

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-Objectを使用すると、continueはパイプラインループから抜け出すようです。

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

私の質問は、ForEach-Objectを実行している間に継続的な動作を取得できるので、パイプラインを分割する必要はありませんか?

111
Justin Dearing

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コマンドレットを使用してパイプラインに変換したい場合があります(エイリアスforeachもあり、この変換が簡単になり、ミスも簡単になります)。すべてのcontinuereturnに置き換える必要があります。

追伸残念ながら、ForEach-Objectbreakをシミュレートするのはそれほど簡単ではありません。

146
Roman Kuzmin

For-Eachオブジェクトはコマンドレットであり、ループではなく、続行/中断が適用されないためです。

たとえば、次の場合:

$b = 1,2,3

foreach($a in $b){

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

write-Host "after"

}

次のように出力されます。

1
after
3
after

これは、foreach-objectコマンドレットではなく、外側のforeachループにcontinueが適用されるためです。ループが存在しない場合、最も外側のレベルであるため、ブレークのように動作する印象を与えます。

それでは、どのように行動のように継続しますか? 1つの方法は、where-object ofcourseです。

1..100 | ?{ $_ % 7  -eq 0} | %{write-Host $_ is a mutliple of 7}
17
manojlds

別の代替手段は一種のハックですが、1回実行するループでブロックをラップすることができます。そうすることで、継続することで望ましい効果が得られます。

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

単純な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"}}
0
Alvin