この最小限の例を考えると
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
出力してLINE 1
1秒後LINE 2
、予想通り。
パイプで接続するとgrep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
動作は前の場合と同じです。予想通り。
またはパイプで接続するとcat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
行動はまた同じで、予想通り。
しかし、、 でパイプしgrep LINE
てから にパイプするとcat
、
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
1秒が経過するまで出力がなく、両方の行が出力にすぐに表示されます。期待しない。
なぜこれが起こるのですか?最後のバージョンが最初の3つのコマンドと同じように機能するようにするにはどうすればよいですか?
答え1
(少なくともGNU)grep
の出力が端末でない場合、出力をバッファリングしますが、これがまさにあなたが見ている動作の原因です。grep
GNUオプションを使用してこの機能を無効にすることができます--line-buffered
。
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep --line-buffered LINE | cat
またはstdbuf
ユーティリティ:
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | stdbuf -oL grep LINE | cat
パイプラインでバッファリングをオフにするこのトピックについては、より多くの内容があります。
答え2
簡単な説明
多くのユーティリティと同様に、これは特定のプログラムに固有のものではありませんgrep
。ラインバッファそして完全にバッファリング。前者の場合、Cライブラリは、このデータを保持するバッファがいっぱいになるか、改行文字が追加されるまで(またはプログラムが完全に終了するまで)出力データをメモリにバッファリングし、実際にバッファ内容を書き込むwrite()
ために呼び出されます。後者の場合、メモリバッファがいっぱいになった場合(またはプログラムがきちんと終了した場合)にのみ実行されますwrite()
。
詳細な説明
これはよく知られていますが、少し間違った解釈です。実際、標準出力はラインバッファリングされません。スマートバッファリングGNU CライブラリとBSD Cライブラリにあります。標準出力は返品標準を読むと顔が赤くなる入力する排ガスそれメモリバッファ(入力の前に読み取る)とCライブラリは、read()
より多くの入力を得るために呼び出す必要がありますそして新しい行の先頭を読んでいます。 (これの1つの理由は、他のプログラムがフィルタの両端に接続され、フィルタへの書き込みとフィルタからの読み出しを交互に行え、1行ずつ動作できるようにしたいときに発生するデッドロックを防ぐためです。 )の「コプロセス」awk
。
Cライブラリの影響
grep
他のユーティリティは、検出された標準出力、より厳密に言えば、これを実行するために使用するCライブラリに基づいてこれを行います。これはCプログラミングの定義機能だからです。対話型デバイスでない場合は完全バッファリングを選択し、そうでない場合はスマートバッファリングを選択します。パイプは対話型デバイスとは見なされません。なぜなら、少なくともUnixとLinuxの世界では、対話型デバイスの定義は、基本的にisatty()
関連ファイル記述子からtrueを返す呼び出しであるからです。
フルバッファリングを無効にする回避策
一部のユーティリティには、この決定を変更するオプションなどgrep
の特別なオプションがあります--line-buffered
が、名前が正しくありません。ただし、実際に使用できるフィルタリングプログラムのうち、これらのオプションを提供するプログラムはほとんどありません。
より一般的には、Cライブラリの特定の内部を掘り下げて決定を変更するツールを使用することが可能です。変更するプログラムがset-UIDであり、特定のCライブラリに固有のものである場合、セキュリティ上の問題があります。 、そしてこれは、Cで書かれた、またはCの上に階層化されたプログラムまたは同様のptybandage
ツールにのみ当てはまります。欲しくないプログラムの内部構造を変更しますが、単に擬似端末を標準出力に挿入して、決定が「対話型」形式で表示されるようにすることでこれに影響します。
追加読書
答え3
使用
grep --line-buffered
grepが一度に複数の行をバッファリングしないようにします。