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

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

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

コマンドパイプラインは、ファイル(テキスト、文字列の配列)を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

そうすべきですか?なぜ一行ずつ?

答え1

stdioほとんどのUNIXプログラムで使用されるC標準I / Oライブラリ()は、共通のバッファリング規則に従います。出力が端末に移動すると、各行の末尾でフラッシュされます。それ以外の場合は、バッファ(Linux / amd64システムでは8K、ユーザーシステムでは異なる場合があります)がいっぱいの場合にのみフラッシュされます。

すべてのユーティリティの一般的な規則に従って、すべての例(、および)でcat|sed出力cat|tr遅延を確認できますcat|tr|sed。 1つの例外があります。 GNU はcat出力をバッファリングしません。未使用またはデフォルトのstdioバッファstdioリングポリシーが変更されます。

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

catしたがって、左側にパイプがある限り、パイプを介したデータ転送はGNUシステムで遅延されません。一行ずつ実行するのではなく、cat端末で行う作業です。 catへの入力を入力すると、端末はバックスペースやCtrl-Uなどの編集キーを使用して「標準」モードの行ベースに設定され、sendを使用する前に入力した行を編集する機会を提供しますEnter

例では、cat|tr|sedを押すたびにデータは引き続き受信されますが、デフォルトのポリシーにtr従います。その出力はパイプに送られるので、各行の後にフラッシュされません。バッファがいっぱいになったり、EOFが受信された場合(最初に発生した場合)、2番目のパイプに書き込みます。catEntertrstdio

sedまた、stdioデフォルトのポリシーに従いますが、出力は端末に送信されるため、完了するとすぐに各行が書き込まれます。これは、パイプの反対側に何かが表示される前に入力する必要がある量に影響します。出力がブロックバッファリングされている場合は、出力バッファを埋めるsedために2倍の入力が必要です。trそして 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'

答え2

実際に理解するには少し考えが必要で、答えにはもっと時間がかかりました。良い質問です(次に投票します)。

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

編集する:

アップを見ました。説明を提供最後の編集内容を入力しています。ありがとうございます!

関連情報