ターミナルでいくつかのコマンドを実行していたところ、パイプコマンドを実行するときにUnix/Linuxはショートカットを使用するのでしょうか?
たとえば、100万行のファイルがあり、その最初の10行にhello world
が含まれているとします。コマンドgrep "hello world" file | head
を実行すると、最初のコマンドは10行が見つかるとすぐに停止しますか、それともファイル全体を最初に検索し続けますか?
ある種。シェルは、実行しているコマンドが何を実行するかを認識していません。一方の出力をもう一方の入力に接続するだけです。
grep
が「helloworld」という10行を超える行を見つけた場合、head
には必要な10行すべてが含まれ、パイプを閉じます。これにより、grep
がSIGPIPEで強制終了されるため、非常に大きなファイルのスキャンを続行する必要はありません。
プログラムがパイプに書き込もうとし、そのパイプから読み取るプロセスがない場合、ライタープログラムは [〜#〜] sigpipe [〜#〜] シグナルを受信します。プログラムがSIGPIPEを受信したときのデフォルトのアクションは、プログラムを終了することです。プログラムはSIGPIPEシグナルを無視することを選択できます。その場合、書き込みはエラー(EPIPE
)を返します。
あなたの例では、これが何が起こるかのタイムラインです:
grep
コマンドとhead
コマンドは並行して起動します。grep
は入力を読み取り、処理を開始します。grep
は出力の最初のチャンクを生成します。head
はその最初のチャンクを読み取り、書き込みます。grep
が最初に終了する可能性があります)、最終的にhead
は必要な行数を出力します。この時点で、head
が終了します。grep
プロセスとhead
プロセスの相対速度によっては、grep
がデータを蓄積し、まだ印刷していない場合があります。 head
が終了するとき、grep
は入力を読み取っている、または内部処理を行っている可能性があります。その場合、引き続き処理が行われます。grep
は処理されたデータを書き出します。その時点で、それはSIGPIPEを受け取り、死にます。grep
は、厳密に必要な入力よりも少し多くの入力を処理する可能性がありますが、通常は数キロバイトしか処理しません。
head
は通常、数キロバイトのチャンクを読み込みます(これは、バイトごとにread
システムコールを発行するよりも効率的であるためです。この動作はバッファリングと呼ばれます)。したがって、後の最後のチャンクの残りは必要な最後の行は破棄されます。grep
は、出力チャンクになる準備ができているデータを蓄積している可能性があります(再度バッファリングします)。出力バッファをフラッシュしようとすると、SIGPIPEを受け取ります。全体として、システムは、フィルタリングユーティリティが自然に効率的に動作するように正確に設計されています。出力チャネルが停止したときに続行する必要があるプログラムは、SIGPIPE信号を無視する手順を実行する必要があります。
パイプラインは次のように機能します。最初に最初のコマンドを実行し、次に2番目のコマンドを実行します。
つまり、A|B
与えられたコマンドである。次に、A
またはB
のどちらが最初に開始するかは不明です。複数のCPUがある場合、それらはまったく同時に起動する可能性があります。パイプは、未定義ですが有限量のデータを保持できます。
Bがパイプから読み取ろうとしたが、利用可能なデータがない場合、B
はデータが到着するまで待機します。 B
がディスクから読み取っていた場合、B
でも同じ問題が発生する可能性があり、ディスクの読み取りが完了するまで待機する必要があります。より近い例えは、キーボードからの読み取りです。そこで、B
はユーザーが入力するのを待つ必要があります。ただし、これらすべての場合において、Bは「読み取り」操作を開始しており、終了するまで待機する必要があります。しかし、B
がA
の部分的な出力のみを必要とするようなコマンドである場合、B
sの入力レベルに達した特定のポイントの後、A
はによって強制終了されます。 SIGPIPE
A
がパイプに書き込もうとし、パイプがいっぱいになった場合、A
はパイプ内の空きができるまで待つ必要があります。 A
は、端末に書き込んでいる場合にも同じ問題が発生する可能性があります。端末にはフロー制御があり、データのペースを調整できます。いずれにせよ、A
に対して、「書き込み」操作を開始し、書き込み操作が終了するまで待機します。
A
とB
はコプロセスとして動作しますが、すべてのコプロセスがパイプと通信するわけではありません。どちらも他方を完全に制御することはできません。
grep
はパイプを直接制御しません(データを受信するだけです)。また、パイプはgrep
を直接制御しません(データを送信するだけです)。
grep
または他のプログラムが行うことは、完全にそのプログラムの内部ロジック次第です。コマンドラインオプションを介してgrep
に早期にexit-when-foundを作成するように指示すると、そうなります。そうでない場合は、ファイルの最後に移動してパターンを探します。 ..
同様に、ターミナルはgrep
の内部動作とShell
の配管動作から完全に切り離されています...ターミナルは基本的に単なる発射台であり、出力ディスプレイです...