PowerShellでパイプラインが中断するとき何がおこるか
[質問]
たとえばLinuxのシェルのコマンドラインで、プロセスをパイプでつなぐ場合、下流のプロセスが終了すると上流プロセスにシグナルで「パイプが中断した」という情報が伝わります。PowerShellのパイプラインでは、上流はどのようにしてパイプラインが閉じたことを知るのですか。
[回答] Gemini
非常に鋭い視点ですね。Linux(POSIX)の世界では、パイプの下流が死ぬと上流プロセスに SIGPIPE シグナルが送られ、EPIPE エラーで書き込みが失敗するという「OSレイヤーの仕組み」があります。
一方、PowerShellのパイプラインは「プロセス間のバイトストリーム」ではなく、.NETオブジェクトのメソッド呼び出しの連鎖として実装されています。
上流が「閉じたこと」を知る仕組み:停止例外(Stopping Exception)
PowerShellでは、下流のコマンドレット(例:Select-Object -First 5)が「もうデータはいらない」と判断すると、内部的に PipelineStoppedException という特殊な例外(または停止通知)を上流に投げます。
具体的に上流が停止を知るプロセスは以下の通りです。
- 下流の判断:
Select-Object -First 5が5つ目のデータを受け取ると、自身の処理を完了させ、パイプラインを「停止状態」にします。 - 上流への通知: 下流コマンドレットが
PipelineStoppedExceptionをスロー(またはそれに準ずるフラグをセット)します。 - 上流の検知: 上流のループ(
forやForEach-Object)が次のオブジェクトをWriteObject()メソッドで出力しようとした瞬間、その例外をキャッチします。 - クリーンアップ: 上流コマンドレットは例外を受けて、自身の
EndProcessingブロック(またはfinallyブロック)を実行し、安全に終了します。
実験:上流が止まったことを証明する
以下のコードを実行すると、Select-Object が止めた瞬間に上流の for ループも停止することが確認できます。
& {
try {
for ($i = 1; ; $i++) {
Write-Host "上流: $i を送信中..." -ForegroundColor Gray
$i # パイプラインに流す
}
}
finally {
Write-Host "上流: パイプラインの切断を検知して終了します。" -ForegroundColor Cyan
}
} | Select-Object -First 3
実行結果:
3を送信した直後、Select-Objectが「完了」を宣言。- 上流の
finallyブロックが走り、無限ループが止まる。
Linuxとの決定的な違い
| 比較項目 | Linux (bash/zshなど) | PowerShell |
|---|---|---|
| 伝達手段 | SIGPIPE (OSシグナル) |
.NET Exception (例外処理) |
| データの単位 | バイト / テキスト | .NET オブジェクト |
| 制御の主体 | カーネルがパイプを管理 | PowerShellエンジンが列挙子(Enumerator)を管理 |
まとめ
PowerShellの上流プロセスは、「次にオブジェクトを渡そうとした時(WriteObject実行時)に、下流が受け取りを拒否したこと(例外)」をもって、パイプラインが閉じたことを知ります。
これにより、無限ループを書いても Select-Object -First などと組み合わせるだけで、リソースを無駄にせず安全に停止できる設計になっています。
この「オブジェクトベースのパイプライン」の挙動について、さらに深掘りしたい部分はありますか?