パイプ、データはパイプラインでどのように流れますか?


22

パイプラインでデータがどのように流れるのか理解していないので、誰かがそこで何が起こっているのかを明確にしたいと思っています。

コマンドのパイプラインは、ファイル(テキスト、文字列の配列)を1行ずつ処理します。(各コマンド自体が行ごとに機能する場合。)テキストの各行がパイプラインを通過する場合、コマンドは前の入力が入力全体の処理を完了するまで待機しません。

しかし、そうではないようです。

これがテスト例です。テキストのいくつかの行があります。それらを大文字にして、各行を2回繰り返します。私はそうしcat text | tr '[:lower:]' '[:upper:]' | sed 'p'ます。

プロセスを追跡するために、「インタラクティブ」に実行できますcat。入力ファイル名をスキップします。パイプラインの各部分は、行ごとに実行されます。

$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2

しかし、完全なパイプラインは、入力が完了するのを待ってからEOF結果を出力するだけです。

$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D

そうなるはずですか?なぜ行ごとではないのですか?


それはパイプではなく、catstdinが閉じるまでバッファリングします。
-goldilocks

しかしtr、stdinが閉じる前sedからプロセス行を実行するcat
-xealits

stdioが使用するデフォルト(前述のすべてのプログラムが使用すると考えられます)は、stderrがバッファリングされず、端末への書き込み時にstdoutが行バッファリングされ、そうでない場合は完全にバッファリングされます(たとえば、ファイルまたはパイプに書き込む場合) 。一部のコマンドには、stdoutバッファリングを変更できるフラグがありますが、trにはないようです。
カスペルド

回答:


36

stdioほとんどのUNIXプログラムが使用するC標準I / Oライブラリ()が後に続く一般的なバッファリングルールがあります。出力が端末に送られる場合、各行の終わりにフラッシュされます。それ以外の場合は、バッファー(Linux / amd64システムでは8K。ユーザーによって異なる場合があります)がいっぱいになった場合にのみフラッシュされます。

すべてのユーティリティは一般的なルールを次れた場合は、出力は(あなたの例の全てで遅延見るであろうcat|sedcat|trcat|tr|sed)。ただし、例外がありcatます。GNUは出力をバッファリングしません。使用しないかstdio、デフォルトのstdioバッファリングポリシーを変更します。

cat他のUnix catはこのように振る舞わないので、他のUnixではなくGNU を使用していると確信できます。従来のUNIXにcatは、-uバッファなしの出力を要求するオプションがあります。GNU catは、-u出力が常にバッファリングされないため、オプションを無視します。

そのcatため、GNUシステムで左側にがあるパイプがあるときはいつでも、パイプを通るデータの通過は遅延しません。cat偶数ラインでラインを行くのではありません-あなたの端末がそれをやっています。catの入力を入力している間、端末は「標準」モード(行ベース)であり、バックスペースやctrl-Uなどの編集キーを使用して、送信前に入力した行を編集できますEnter

このcat|tr|sed例でtrcat、を押すとすぐにデータを受信しますEntertrstdioデフォルトのポリシーに従っています。その出力はパイプに送られるため、各行の後にフラッシュしません。バッファがいっぱいになったとき、またはEOFを受信したときのどちらか早い方で、2番目のパイプに書き込みます。

sedまた、stdioデフォルトのポリシーに従っていますが、その出力は端末に送られるため、終了するとすぐに各行に書き込まれます。これは、あなたがパイプラインのもう一方の端に何かショーの前まで入力する必要がありますどのくらいの効果がある-場合はsed、その出力をブロックバッファリングされた、あなたは(塗りつぶしに限り二度入力する必要があるだろうtr"出力バッファS sed出力さんバッファ)。

GNUにsed-uオプションがあるため、順序を逆にして使用cat|sed -u|trすると、出力がすぐに表示されます。(sed -uオプションは他の場所で利用可能かもしれませんが、私はそれがのような古代のUnixの伝統だとは思いませんcat -u)私が知る限り、に同等のオプションはありませんtr

デフォルトstdbufを使用するコマンドのバッファリングモードを変更できるユーティリティがありますstdioLD_PRELOADCライブラリがサポートするように設計されていないことを達成するために使用するため、少し脆弱ですが、この場合は動作するようです:

cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'

1
ありがとう!素晴らしい答え。おそらく私はそれを見つけることができるように、質問で何らかの形でバッファリングに言及する必要があります。
xealits

teeそしてddまた、通常、独自のルールで遊びます。想像力をかき立てると、3つのツールはstdbuf、バックグラウンドのパイプラインでの必要性をかなり移植的に無効にすることができます。
mikeserv

1
これは、catの無駄な使用を避ける理由の1つです。
ホッブズ

8

実際、これには理解するためのいくつかの考えがあり、さらに答える必要がありました。すばらしい質問です(次に投票します)。

tr | sed上記のデバッグ項目を試してみませんでした:

>tr '[:lower:]' '[:upper:]' | sed 'p'
i am writing
still writing
now ctrl-d
I AM WRITING
I AM WRITING
STILL WRITING
STILL WRITING
NOW CTRL-D
NOW CTRL-D
>

明らかにtrバッファ。毎日新しいことを学びましょう!

編集

私はこれについて考えているように、原因を特定しましたが、説明を提供していません。あなたの場合cat | tr、すぐに書き込みます。あなたの場合、すぐに書き込みますcat | sedが、あなたの場合tr | sed待機EOFます。私は答えはで埋葬される可能性があります示唆しているtrか、sedそして、ソースコード、およびパイプの問題ではありません。

編集

最後の編集を入力している間 Wumpus が説明提供してくれました。ありがとう!


1
確かに彼らはバッファリングします!Wumpusが述べたように、およそ8kbの行でのテストでは、バッファーが実際に8Kbであることが示されています。両方の回答を受け入れて評判を共有したいのですが、Wumpusをより完全なものとして扱います。とにかくありがとう!
xealits

1
問題ありません。私の答えは経験的な答えでした。彼は知識のある答えでした。
ポアソンエアロヘッド

stdbuf役に立つかもしれない使用方法を示すこの質問も参照してください。unix.stackexchange.com/questions/182537/…-
ジョー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.