この最小限の例を考えると
( 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つのコマンドと同じように動作させるにはどうすればよいですか?
(少なくともGNU)grep
の出力がターミナルではない場合、それは出力をバッファリングします。これが、表示されている動作の原因です。 GNU grep
's --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
パイプでのバッファリングをオフにする このトピックに関する詳細があります。
多くのユーティリティと同様に、これは1つのプログラムに固有のものではありません。grep
は、標準出力をline bufferedと完全にバッファリング。前者の場合、Cライブラリは、出力データを保持するバッファーがいっぱいになるか、改行文字が追加される(またはプログラムが正常に終了する)まで、出力データをメモリーにバッファーし、その後、実際にwrite()
を呼び出します。バッファの内容を書き込みます。後者の場合、メモリ内バッファーがいっぱいになる(またはプログラムが正常に終了する)だけがwrite()
をトリガーします。
これはよく知られた説明ですが、少し間違っています。実際、標準出力は行バッファリングされていませんが、GNU CライブラリおよびBSD Cライブラリではsmart buffered)です。出力はまた、標準を読み取るときにフラッシュされますinput排気its(先読み入力の)メモリ内バッファとCライブラリはread()
を呼び出して、さらに入力をフェッチする必要がありますand新しい行の先頭を読み取っています(これの1つの理由は、別のプログラムがフィルターの両端に接続し、操作できることを期待しているときにデッドロックを防ぐためです)フィルターへの書き込みとフィルターからの読み取りを交互に1行ずつ、たとえばGNU awk
などの "coprocesses")
grep
および他のユーティリティがこれを行います。または、厳密には、使用するCライブラリがこれを行います。これは、Cでのプログラミングの定義済み機能であるため、標準出力が検出された内容に基づいているためです。 。インタラクティブデバイスではない場合に限り、フルバッファリングを選択します。それ以外の場合は、スマートバッファリングを選択します。少なくともUnixおよびLinuxの世界では、インタラクティブデバイスであることの定義は本質的にisatty()
呼び出しであり、関連するファイル記述子に対してtrueを返すため、パイプはインタラクティブデバイスではないと見なされます。
grep
のような一部のユーティリティには、この決定を変更する--line-buffered
などの特異なオプションがあります。しかし、実際に使用できるフィルタープログラムのごくわずかな部分に、このようなオプションがあります。
より一般的には、Cライブラリの特定の内部を掘り下げ、その意思決定を変更するツールを使用できます(変更されるプログラムがset-UIDである場合にセキュリティ上の問題があり、特定のCライブラリに固有であり、実際にC言語で記述された、またはC言語の上に階層化されたプログラムに固有)、またはptybandage
などのツールの内部を変更しないプログラムに影響を与えるために、決定は「対話型」として出てくるように、標準出力として疑似端末を単に挿入します。
使用する
grep --line-buffered
grepが一度に複数の行をバッファリングしないようにします。