2つのコマンドを呼び出すスクリプトがあります。
long_running_command | print_progress
long_running_command
は進捗状況を出力しますが、満足していません。 print_progress
を使用して見栄えをよくしています(つまり、進行状況を1行で出力しています)。
問題:パイプをstdoutに接続すると4Kバッファもアクティブになるため、Nice印刷プログラムは何も取得しません...何も...何も取得しません...全体ロット... :)
4K
のlong_running_command
バッファを無効にするにはどうすればよいですか(いいえ、ソースがありません)?
unbuffer
コマンド(これは expect
パッケージの一部として提供されます)を使用できます。
unbuffer long_running_command | print_progress
unbuffer
は、疑似端末(pty)を介してlong_running_command
に接続します。これにより、システムはそれを対話型プロセスとして扱うため、遅延の原因となる可能性のあるパイプラインでの4 KiBバッファリングを使用しません。 。
より長いパイプラインの場合、各コマンド(最後のコマンドを除く)のバッファーを解除する必要がある場合があります。
unbuffer x | unbuffer -p y | z
この猫のスキンを作成する別の方法は、GNU Coreutilsの一部である stdbuf
プログラムを使用することです(FreeBSDにも独自のプログラムがあります)。
_stdbuf -i0 -o0 -e0 command
_
これにより、入力、出力、エラーのバッファリングが完全にオフになります。一部のアプリケーションでは、パフォーマンス上の理由から、ラインバッファリングの方が適している場合があります。
_stdbuf -oL -eL command
_
動的にリンクされたアプリケーションのstdio
バッファリング(printf()
、fputs()
...)でのみ機能し、そのアプリケーションが他の方法でバッファリングを調整しない場合にのみ注意してください。標準ストリームのそれ自体で、ほとんどのアプリケーションをカバーするはずです。
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
grep
、sed
およびawk
の場合、出力を強制的にラインバッファリングできます。以下を使用できます。
grep --line-buffered
出力を強制的に行バッファリングします。デフォルトでは、標準出力がターミナルである場合、出力はラインバッファリングされ、それ以外の場合はブロックバッファリングされます。
sed -u
出力行をバッファリングします。
詳細については、このページを参照してください: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
出力が端末に送信されないときにlibcがそのバッファリング/フラッシュを変更することに問題がある場合は、 socat を試してください。ほぼすべての種類のI/Oメカニズム間で双方向ストリームを作成できます。それらの1つは、疑似ttyと話すフォークされたプログラムです。
socat EXEC:long_running_command,pty,ctty STDIO
それは何ですか
これによりlong_running_command
と同じ出力が得られる場合は、パイプを続行できます。
編集:うわー、アンバッファーの答えが見つかりませんでした!まあ、とにかくsocatは素晴らしいツールなので、この答えはそのままにしておく
使用できます
long_running_command 1>&2 |& print_progress
問題は、libcがstdoutを画面に表示するときにラインバッファーを使用し、stdoutをファイルに出力するときにフルバッファーを使用することです。しかし、stderrのバッファーはありません。
それはパイプバッファの問題ではないと思います。libcのバッファポリシーがすべてです。
以前はそうでしたが、おそらくまだそうですが、標準出力が端末に書き込まれると、デフォルトで行バッファリングされます。改行が書き込まれると、その行は端末に書き込まれます。標準出力がパイプに送信されると、それは完全にバッファーされます。したがって、データは、標準I/Oバッファーがいっぱいになったときにのみパイプラインの次のプロセスに送信されます。
それが問題の原因です。パイプに書き込むプログラムを変更せずに修正できることがたくさんあるかどうかはわかりません。 __IOLBF
_フラグ付きのsetvbuf()
関数を使用して、stdout
を無条件にラインバッファーモードにできます。しかし、それをプログラムに強制する簡単な方法はわかりません。または、プログラムは適切なポイント(出力の各行の後)でfflush()
を実行できますが、同じコメントが適用されます。
パイプを疑似ターミナルに置き換えた場合、標準のI/Oライブラリは出力がターミナルであると見なし(ターミナルのタイプであるため)、自動的にラインバッファーを生成すると思います。しかし、それは物事を処理する複雑な方法です。
私はこれが古い質問であり、すでに多くの回答があったことを知っていますが、バッファの問題を回避したい場合は、次のようなことを試してください:
stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt
これにより、ログがリアルタイムで出力され、logs.txt
ファイルに保存されます。バッファはtail -f
コマンドに影響しなくなります。
問題はパイプにあるとは思いません。長時間実行しているプロセスが独自のバッファを頻繁にフラッシュしていないようです。パイプのバッファーサイズを変更することは、それを回避するためのハックになりますが、カーネルを再構築しないと可能ではないと思います。おそらく他の多くのプロセスに影響を与えるため、ハックとして実行したくないものです。
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
この投稿はここ によると、パイプのulimitを1つの512バイトブロックに減らすことができます。それは確かにバッファリングをオフにしませんが、まあ、512バイトは4Kよりはるかに少ないです:3
この賢い解決策を見つけました:(echo -e "cmd 1\ncmd 2" && cat) | ./Shell_executable
これでうまくいきます。 cat
は、追加の入力を読み取り(EOFまで)、echo
がShell_executable
の入力ストリームに引数を入れた後に、パイプに渡します。
this によると、パイプバッファーサイズはカーネルで設定されているようであり、変更するにはカーネルを再コンパイルする必要があります。