web-dev-qa-db-ja.com

PowerShellでForEach-Objectを終了する方法

私は次のコードを持っています:

$project.PropertyGroup | Foreach {
    if($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) {
        $a = $project.RemoveChild($_);
        Write-Host $_.GetAttribute('Condition')"has been removed.";
    }
};

質問#1:ForEachを終了するにはどうすればよいですか? 「break」と「continue」を使用してみましたが、機能しません。

質問#2:foreachループ内でリストを変更できることがわかりました... C#ではそのようにできません...なぜPowerShellでできるのですか?

50
Michael Sync

アイテム#1。 breakループ内にforeachを配置すると、ループは終了しますが、パイプラインは停止しません。次のようなものが欲しいようです:

$todo=$project.PropertyGroup 
foreach ($thing in $todo){
    if ($thing -eq 'some_condition'){
        break
    }
}

アイテム#2。 PowerShellでは、その配列に対するforeachループ内の配列を変更できますが、ループを終了するまでそれらの変更は有効になりません。例として以下のコードを実行してみてください。

$a=1,2,3
foreach ($value in $a){
  Write-Host $value
}
Write-Host $a

PowerShellの作成者がこれを許可した理由についてはコメントできませんが、他のほとんどのスクリプト言語( PerlPython およびShell)は同様の構成を許可します。

88
smeltplate

ForEach-Objectが属するパイプラインを停止するには、ForEach-Objectの下のスクリプトブロック内でステートメントcontinueを使用します。 continueは、foreach(...) {...}ForEach-Object {...}で使用すると動作が異なります。これが可能である理由です。元のオブジェクトの一部を破棄してパイプラインでオブジェクトの生成を続けたい場合、Where-Objectを使用して除外するのが最善の方法です。

8
darlove

foreachforeach-objectには違いがあります。

ここで見つけることができる非常に良い説明: MS-ScriptingGuy

PSでのテストについては、ここに違いを示すスクリプトがあります。

ForEach-Object:

# Omit 5.
1..10 | ForEach-Object {
if ($_ -eq 5) {return}
# if ($_ -ge 5) {return} # Omit from 5.
Write-Host $_
}
write-Host "after1"
# Cancels whole script at 15, "after2" not printed.
11..20 | ForEach-Object {
if ($_ -eq 15) {continue}
Write-Host $_
}
write-Host "after2"
# Cancels whole script at 25, "after3" not printed.
21..30 | ForEach-Object {
if ($_ -eq 25) {break}
Write-Host $_
}
write-Host "after3"

foreach

# Ends foreach at 5.
foreach ($number1 in (1..10)) {
if ($number1 -eq 5) {break}
Write-Host "$number1"
}
write-Host "after1"
# Omit 15. 
foreach ($number2 in (11..20)) {
if ($number2 -eq 15) {continue}
Write-Host "$number2"
}
write-Host "after2"
# Cancels whole script at 25, "after3" not printed.
foreach ($number3 in (21..30)) {
if ($number3 -eq 25) {return}
Write-Host "$number3"
}
write-Host "after3"
5
Stoffi

パイプラインから抜け出す簡単な方法はありませんが、Where-Objectを使用してフィルターを適用できます。可能な場合、最良の解決策は、Foreach-Object(パイプを使用してオブジェクトが渡される)を標準のForeachループ構造に変換することです。次に例を示します。

Foreach-Object(パイプラインでオブジェクトを渡す):

1..10 | Foreach-Object {
     if ($_ -eq 3) {return;}
     £_;
}
Write-Host "Only skips one iteration (iterates all 10 times)";

標準のForeachループ構造に変換:

Foreach ($i in 1..10) { 
     if ($i -eq 3) {break;}
     $i;
}
Write-Host "Ends the loop after 2nd iteration (iterates only 2 times)";

Where-Objectフィルターを使用した場合も同じです(Foreachループへの変換は不可能です):

1..10 | Where-Object {$_ -le 2} | Foreach-Object {
     $_;
}
Write-Host "Ends the loop after 2nd iteration (iterates only 2 times)";

HTH

0
Eddie Kumar