web-dev-qa-db-ja.com

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

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

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

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

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

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

$ 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

そうなのでしょうか?なぜ行ごとではないのですか?

22
xealits

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

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

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

したがって、左側にcatがあるパイプがある場合、GNUシステムでは、パイプを介したデータの通過は遅延されません。catは1行ずつも実行されません-ターミナルはそれを行っています。猫の入力を入力している間、ターミナルは「標準」モードです-行ベースで、バックスペースやctrl-Uなどの編集キーを使用して、入力した行を編集してから送信する機会 Enter

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

sedstdioのデフォルトポリシーに従っていますが、その出力はターミナルに送られるため、各行が終了するとすぐに各行に書き込まれます。これは、何かがパイプラインのもう一方の端に表示される前に入力する必要がある量に影響します。sedがその出力をブロックバッファリングしている場合は、2倍入力する必要があります(trの出力バッファandsedの出力バッファ)。

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

stdbufと呼ばれるユーティリティがあり、stdioのデフォルトを使用するコマンドのバッファリングモードを変更できます。 LD_PRELOADを使用してCライブラリがサポートするように設計されていない機能を実行するため、少し脆弱ですが、この場合は機能するようです。

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

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

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 | sedEOF待ちます。その場合、答えはtrまたはsedソースコードに埋め込まれる可能性があり、パイプの問題ではない可能性があります。

[〜#〜]編集[〜#〜]

最後の編集を入力しているときにWumpusが説明を提供しているのがわかります。ありがとう!

8