web-dev-qa-db-ja.com

パイプのバッファリングをオフにする

2つのコマンドを呼び出すスクリプトがあります。

long_running_command | print_progress

long_running_commandは進捗状況を出力しますが、満足していません。 print_progressを使用して見栄えをよくしています(つまり、進行状況を1行で出力しています)。

問題:パイプをstdoutに接続すると4Kバッファもアクティブになるため、Nice印刷プログラムは何も取得しません...何も...何も取得しません...全体ロット... :)

4Klong_running_commandバッファを無効にするにはどうすればよいですか(いいえ、ソースがありません)?

409
Aaron Digulla

unbuffer コマンド(これは expect パッケージの一部として提供されます)を使用できます。

unbuffer long_running_command | print_progress

unbufferは、疑似端末(pty)を介してlong_running_commandに接続します。これにより、システムはそれを対話型プロセスとして扱うため、遅延の原因となる可能性のあるパイプラインでの4 KiBバッファリングを使用しません。 。

より長いパイプラインの場合、各コマンド(最後のコマンドを除く)のバッファーを解除する必要がある場合があります。

unbuffer x | unbuffer -p y | z
262
cheduardo

この猫のスキンを作成する別の方法は、GNU Coreutilsの一部である stdbuf プログラムを使用することです(FreeBSDにも独自のプログラムがあります)。

_stdbuf -i0 -o0 -e0 command
_

これにより、入力、出力、エラーのバッファリングが完全にオフになります。一部のアプリケーションでは、パフォーマンス上の理由から、ラインバッファリングの方が適している場合があります。

_stdbuf -oL -eL command
_

動的にリンクされたアプリケーションのstdioバッファリング(printf()fputs()...)でのみ機能し、そのアプリケーションが他の方法でバッファリングを調整しない場合にのみ注意してください。標準ストリームのそれ自体で、ほとんどのアプリケーションをカバーするはずです。

474
a3nm

long_running_commandのラインバッファリング出力モードをオンにするさらに別の方法は、long_running_commandを疑似端末(pty)で実行するscriptコマンドを使用することです。

script -q /dev/null long_running_command | print_progress      # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress    # Linux
78
chad

grepsedおよびawkの場合、出力を強制的にラインバッファリングできます。以下を使用できます。

grep --line-buffered

出力を強制的に行バッファリングします。デフォルトでは、標準出力がターミナルである場合、出力はラインバッファリングされ、それ以外の場合はブロックバッファリングされます。

sed -u

出力行をバッファリングします。

詳細については、このページを参照してください: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html

69
yaneku

出力が端末に送信されないときにlibcがそのバッファリング/フラッシュを変更することに問題がある場合は、 socat を試してください。ほぼすべての種類のI/Oメカニズム間で双方向ストリームを作成できます。それらの1つは、疑似ttyと話すフォークされたプログラムです。

 socat EXEC:long_running_command,pty,ctty STDIO 

それは何ですか

  • 疑似ttyを作成する
  • ptyのスレーブ側をstdin/stdoutとしてlong_running_commandをフォークする
  • ptyのマスター側と2番目のアドレス(ここではSTDIO)の間に双方向ストリームを確立します

これによりlong_running_commandと同じ出力が得られる場合は、パイプを続行できます。

編集:うわー、アンバッファーの答えが見つかりませんでした!まあ、とにかくsocatは素晴らしいツールなので、この答えはそのままにしておく

52
shodanex

使用できます

long_running_command 1>&2 |& print_progress

問題は、libcがstdoutを画面に表示するときにラインバッファーを使用し、stdoutをファイルに出力するときにフルバッファーを使用することです。しかし、stderrのバッファーはありません。

それはパイプバッファの問題ではないと思います。libcのバッファポリシーがすべてです。

20
Wang HongQin

以前はそうでしたが、おそらくまだそうですが、標準出力が端末に書き込まれると、デフォルトで行バッファリングされます。改行が書き込まれると、その行は端末に書き込まれます。標準出力がパイプに送信されると、それは完全にバッファーされます。したがって、データは、標準I/Oバッファーがいっぱいになったときにのみパイプラインの次のプロセスに送信されます。

それが問題の原因です。パイプに書き込むプログラムを変更せずに修正できることがたくさんあるかどうかはわかりません。 __IOLBF_フラグ付きのsetvbuf()関数を使用して、stdoutを無条件にラインバッファーモードにできます。しかし、それをプログラムに強制する簡単な方法はわかりません。または、プログラムは適切なポイント(出力の各行の後)でfflush()を実行できますが、同じコメントが適用されます。

パイプを疑似ターミナルに置き換えた場合、標準のI/Oライブラリは出力がターミナルであると見なし(ターミナルのタイプであるため)、自動的にラインバッファーを生成すると思います。しかし、それは物事を処理する複雑な方法です。

11

私はこれが古い質問であり、すでに多くの回答があったことを知っていますが、バッファの問題を回避したい場合は、次のようなことを試してください:

stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt

これにより、ログがリアルタイムで出力され、logs.txtファイルに保存されます。バッファはtail -fコマンドに影響しなくなります。

7
Marin Nedea

問題はパイプにあるとは思いません。長時間実行しているプロセスが独自のバッファを頻繁にフラッシュしていないようです。パイプのバッファーサイズを変更することは、それを回避するためのハックになりますが、カーネルを再構築しないと可能ではないと思います。おそらく他の多くのプロセスに影響を与えるため、ハックとして実行したくないものです。

5
anon

chad's answer と同様に、次のような小さなスクリプトを書くことができます。

# save as ~/bin/scriptee, or so
script -q /dev/null sh -c 'exec cat > /dev/null'

次に、このscripteeコマンドをteeの代わりに使用します。

my-long-running-command | scriptee

悲しいかな、私はそのようなバージョンをLinuxで完全に動作させるようには思えないので、BSDスタイルのUNIXに限定されているようです。

Linuxでは、これは間近ですが、終了するときに(Enterキーを押すまでなど)プロンプトが表示されません...

script -q -c 'cat > /proc/self/fd/1' /dev/null
3
jwd

この投稿はここ によると、パイプのulimitを1つの512バイトブロックに減らすことができます。それは確かにバッファリングをオフにしませんが、まあ、512バイトは4Kよりはるかに少ないです:3

2
RAKK

この賢い解決策を見つけました:(echo -e "cmd 1\ncmd 2" && cat) | ./Shell_executable

これでうまくいきます。 catは、追加の入力を読み取り(EOFまで)、echoShell_executableの入力ストリームに引数を入れた後に、パイプに渡します。

1
jaggedsoft

this によると、パイプバッファーサイズはカーネルで設定されているようであり、変更するにはカーネルを再コンパイルする必要があります。

0
second