「継続」が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"
}

continueForEach-Objectを実行しているときに-のような動作が得られるので、パイプラインを分割する必要はありませんか?


これは、たくさんのコマンドを使用するページforeachです。techotopia.com
index.php

ここでは、まともな説明とサンプルを見つけた... powershell.com/cs/blogs/tips/archive/2015/04/27/...
ネイサンハートレイ

回答:


164

return代わりにを使用するだけcontinueです。これreturnForEach-Object、特定の反復で呼び出されるスクリプトブロックから返されるためcontinue、ループをシミュレートします。

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

リファクタリングの際に注意すべき落とし穴があります。コマンドレットを使用してforeachステートメントブロックをパイプラインに変換したい場合がありますForEach-Objectforeachこの変換を簡単にし、間違いを簡単にするためのエイリアスもあります)。すべてcontinueのをに置き換える必要がありreturnます。

PS:残念ながら、それは簡単にシミュレートすることではないbreakの中でForEach-Object


2
OPが明らかに言っていることから、continueシミュレートするために使用することができますbreakForEach-Object:)
リチャード・ハウアー

6
@ Richard Hauerこのようなa は、使用されている場所continueだけForEach-Objectでなく、スクリプト全体を壊します。
ローマクズミン2015年

22

そのためFor-Each、オブジェクトがコマンドレットではなくループと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-objectコマンドレットではなく、外部のforeachループに適用されるためです。ループがない場合、最も外側のレベルなので、のように動作しているような印象を与えますbreak

では、どのようにして-のようcontinueな動作をするのですか?もちろん1つの方法はWhere-Objectです。

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

Where-Objectコマンドレットを使用することをお勧めします。私の実際のケースでは、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 '14 / 10/14

3
@manojldsは多分彼はあなたの1行の解決策は「読みにくい」と思っているかもしれません、少なくとも私にとっては完全に反対です。物事を行うためのパイプラインの方法は本当に強力で明確であり、そのような単純なもののための正しいアプローチです。それを利用せずにシェルでコードを書くのは無意味です。
mjsr

私の場合はこれが正解でした。where条件を追加して、最初から処理する必要がないように、続行または戻るオブジェクトを除外します。+1
Chris Magnuson、2015

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
率直に言って、それは醜いです:)そして、ハッチだけではなく、foreachオブジェクトの代わりに、foreachループを使用することもできます。
manojlds 2011年

1
@manojlds:1..100は例示のみを目的としています。{} while($ False)はforループと同じように機能し、少し直感的です。
Harry Martyrossian 2017年

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"}}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.