メインコンテンツまでスキップ

ForEach-Objectにおけるcontinueとbreakについて

· 約4分
mebiusbox
engineer

ForEach-Object内でのcontinuebreakに関する備忘録です.

PowerShellでパイプライン処理のfor文として ForEach-Object をよく使います.これは foreach% のエイリアスがあります. この foreach エイリアスと foreach(in) は別物です.本文では foreach(in) は次のような構文を指しているとします.

foreach ($i in $arr) {
}

ForEach-Object は便利な反面、誤解を招きやすいものだと思います. 実際、AIチャットも見当違いな回答をしていましたし、ネットでも間違った内容が見受けられました. かくいう私も絶対の確信をもっていっているわけではないので、ご了承ください.動作環境はPowerShell7.4.5です.

ForEach-Object に continue や break は使えない

これがすべてです.使えるのは return です.ForEach-Objectはパイプライン処理であって、continuebreakはパイプラインに直接作用しません. continuebreakが作用するのは内側のループ処理に対してです.ちなみに、ループがなければプログラム全体に作用しますので、その場合はプログラムが終了します.

たとえば次のプログラムでは、continue が実行されたときにプログラムが終了します.

test.ps1
1..5 | ForEach-Object {
if ($_ -gt 3) {
continue
}
$_
}
Write-Host "CHECKPOINT"

この出力は次のようになります.

1
2
3

CHECKPOINTは出力されません.これは break でも同じです.

1..5 | ForEach-Object {
if ($_ -gt 3) {
break
}
$_
}
Write-Host "CHECKPOINT"

この場合、次のようにreturnを使うと意図した動作になります.

1..5 | ForEach-Object {
if ($_ -gt 3) {
return
}
$_
}
Write-Host "CHECKPOINT"

この出力は次のようになります.

1
2
3
CHECKPOINT

このコードではすべてのパイプラインが実行されていることに注意してください.5回処理されています. breakのように中断されるわけではありません. continuebreakを使いたい場合は、ループ回数が分かっているなら foreach(in) を使うか for文を使いましょう.

では、パイプライン処理で中断させたいときはどうすればいいでしょうか.1つの方法としてパイプラインの外にループを入れてbreakすることです.

do {
1..5 | ForEach-Object {
if ($_ -gt 3) {
break
}
$_
}
} while ($false)
Write-Host "CHECKPOINT"

この出力は次のようになります.

1
2
3
CHECKPOINT

continuebreakForEach-Objectに対して使いたくなるし、foreachというエイリアスもあって間違えやすいですよね. 以上です.