catを介してパイプされた場合、grepはEOFまで出力されません


19

この最小限の例を考えると

( 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つのコマンドと同じように動作させるにはどうすればよいですか?


catファイルを連結します。パイピングで何をしようとしていcatますか?
ダグラス

15
@DouglasHeld引数なしで呼び出された場合、cat単に読み取りstdin、出力しstdoutます。もちろん、私の代わりに複雑な多くのもので、この質問を思い付いたechocat、これらはアップはるかに簡単な例で問題ショー以来、無関係であることが判明します。
lisyarus

3
@DouglasHeld:猫への配管は、stdoutを強制的に端末にしない場合に便利です。たとえば、これは多くのコマンドが色付き出力を使用しないようにする簡単な方法です。
wchargin

これは、スタックオーバーフローに関する別の質問の複製だと断言します!
iBug

@wcharginどうもありがとう、私が知らなかったposixについて何か新しいことを教えてくれました。
ダグラス

回答:


38

(少なくともGNU)grepの出力が端末ではない場合、出力をバッファリングします。これが表示されている動作の原因です。GNU grep--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

このトピックについては、パイプのバッファリングをオフにしてください


26

簡単な説明

多くのユーティリティと同様に、これは1つのプログラムに固有のものではないため、grepその標準出力は行バッファリング完全バッファリングの間で異なります。前者の場合、Cライブラリは、それらのデータを保持するバッファーがいっぱいになるか、改行文字が追加される(またはプログラムが正常に終了する)まで、出力データをメモリーwrite()にバッファーします。その後、実際にバッファーの内容を書き込むために呼び出します。後者の場合、メモリ内バッファーがいっぱいになる(またはプログラムが正常に終了する)だけでがトリガーされますwrite()

より詳細な説明

これはよく知られているが、少し間違った説明です。実際、標準出力は行バッファーではなく、GNU CライブラリーとBSD Cライブラリーでスマートにバッファーされます。標準出力がされて、標準の読み取り時にフラッシュ入力すると、排気その(先読み入力の)メモリ内のバッファをとCライブラリが呼び出す必要がありますread()いくつかのより多くの入力を取得する、新しい行の先頭を読んでいます。(これの1つの理由は、別のプログラムがフィルターの両端に接続し、フィルターへの書き込みとフィルターからの読み取りを交互に行ごとに操作できることを期待するときのデッドロックを防ぐためです。GNUの「コプロセス」awk例えば。)

Cライブラリの影響

grep他のユーティリティはこれを行います-または、より厳密には、使用するCライブラリがこれを行います。これは、標準出力の検出に基づいて、C言語でのプログラミングの定義済み機能であるためです。インタラクティブなデバイスではない場合(のみ)、完全なバッファリングを選択し、そうでない場合はスマートバッファリングを選択します。少なくともUnixおよびLinuxの世界では、インタラクティブデバイスであるという定義は、本質的isatty()に関連するファイル記述子に対してtrueを返す呼び出しであるため、パイプはインタラクティブデバイスではないと見なされます。

フルバッファリングを無効にするための回避策

このようないくつかのユーティリティにgrep--line-buffered、この決定を変更するような特異なオプションがあります。しかし、実際に使用できるフィルタープログラムのごくわずかな部分には、このようなオプションがあります。

より一般的には、Cライブラリの特定の内部を掘り下げてその意思決定を変更するツールを使用できます(変更されるプログラムがset-UIDであり、特定のCライブラリに固有であり、実際に書かれたプログラムに固有またはC言語の上に階層化)、またはのようなツールはptybandageそれはありませんプログラムの内部を変更するが、決定はに、「インタラクティブ」として出てくるように、単に標準出力として擬似端末を挿入これに影響します。

参考文献


1
「line buffered」というフレーズが誤った名前である場合、それは実際にはのせいでgrepはなく、基礎となるライブラリ呼び出しsetbuf/setvbufのせいです。C規格に関する信頼できるオンラインリファレンスは知りませんが、たとえば、LinuxおよびFreeBSDのマニュアルページsetvbufと「ラインバッファ」と呼ばれるPOSIXの説明があります。それの記号定数でさえです_IOLBF
-ilkkachu

さて、あなたはより良く学んだ。このバッファリング戦略、簡潔ではありますが、GNU Cライブラリdocoで説明されています。 ローラン・ベルコットはこの問題についてもっと率直です。 私もそれを言及しました。
JdeBP

出力バッファリングのこの優れた説明にとって、「あなたの期待は間違っている」とは思いませんでした。削除し、回答の各セクションに説明的な見出しを追加したことを気にしないでください。
アンソニーG-モニカの正義

2
@ilkkachu C標準は実際に「ラインバッファリング」を使用します。パー7.21.3ファイル、段落3:ストリームがバッファリングされていない場合は、ストリームが完全にバッファリングされている場合、ストリームがラインバッファリングされている場合は、「、...、...、文字がするかのようにホスト環境から送信されることが意図されています改行文字に遭遇するとブロックします。...」実際、C標準では、「line buffered」というフレーズを5回使用しています。したがって、それは間違った名前ではありません。
アンドリューヘンレ

1
さらに、私が理解しているように、ここで「スマートバッファリング」として説明されているアプローチは、C標準が「ラインバッファリング」として説明しているように見えます。具体的には、改行でバッファをフラッシュすることに加えて、「ストリームがラインバッファリングされる場合、バッファリングされていないストリームで[...]入力が要求された場合、または入力は、ホスト環境からの文字の送信を必要とする行バッファーストリームで要求されます。」したがって、これはGNUやBSDの癖ではなく、言語が要求するものです。
ジョンボリンジャー

7

つかいます

grep --line-buffered

grepが一度に複数行をバッファリングしないようにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.