stdout
から マニュアルページ:
ストリーム stderr はバッファリングされません。ストリーム stdout は、端末を指す場合は行バッファリングされます .fflush(3) または exit(3) が呼び出されるか、改行が出力されるまで、部分的な行は表示されません。
結論:出力が端末でない限り、プログラムの標準出力はデフォルトで完全にバッファリングされたモードになります。これは基本的に、文字単位は言うまでもなく、行単位ではなく、大きなブロックでデータを出力することを意味します。
これを回避する方法:
-
プログラムを修正する:リアルタイムの出力が必要な場合は、プログラムを修正する必要があります。 C では
fflush(stdout)
を使用できます 各出力ステートメントの後、またはsetvbuf()
標準出力のバッファリングモードを変更します。 Python の場合はsys.stdout.flush()
です -
完全な stdout リダイレクトではなく、PTY から記録できるユーティリティを使用します。 GNU Screen でこれを行うことができます:
screen -d -m -L python test.py
スタートでしょう。これにより、プログラムの出力が
screenlog.0
というファイルに記録されます (または同様の)デフォルトの遅延が10秒の現在のディレクトリで、screen
を使用できます コマンドが実行されているセッションに接続して、入力を提供または終了します。ログファイルの遅延と名前は、構成ファイルで、またはバックグラウンド セッションに接続した後に手動で変更できます。
編集:
ほとんどの Linux システムでは、3 番目の回避策があります。LD_PRELOAD
を使用できます。 変数とプリロードされたライブラリを使用して C ライブラリの選択関数をオーバーライドし、それらを使用して stdout
を設定します。 これらの関数がプログラムによって呼び出されるときのバッファリング モード。この方法はうまくいくかもしれませんが、いくつかの欠点があります:
-
静的実行可能ファイルではまったく機能しません
-
壊れやすく、かなり醜いです。
-
SUID 実行可能ファイルではまったく機能しません。動的ローダーは
LD_PRELOAD
の読み取りを拒否します。 セキュリティ上の理由から、そのような実行可能ファイルをロードするときの変数。 -
壊れやすく、かなり醜いです。
-
後にプログラムによって呼び出されるライブラリ関数を見つけてオーバーライドする必要があります 最初に
stdout
を設定します バッファリング モード、できれば before 任意の出力。getenv()
多くのプログラムに適していますが、すべてではありません。printf()
などの一般的な I/O 関数をオーバーライドする必要がある場合があります またはfwrite()
- プッシュが押し寄せてきた場合、バッファリング モードを制御するすべての関数をオーバーライドし、stdout
の特別な条件を導入する必要がある場合があります。 . -
壊れやすく、かなり醜いです。
-
望ましくない副作用がないことを確認するのは困難です。これを正しく行うには、
stdout
のみを確保する必要があります 影響を受けており、オーバーライドがプログラムの残りの部分をクラッシュさせないことを確認してください。stdout
-
もろくて醜いって言ったっけ?
とはいえ、プロセスは比較的簡単です。 Cファイルを入れます。 linebufferedstdout.c
置換関数:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
char *getenv(const char *s) {
static char *(*getenv_real)(const char *s) = NULL;
if (getenv_real == NULL) {
getenv_real = dlsym(RTLD_NEXT, "getenv");
setlinebuf(stdout);
}
return getenv_real(s);
}
次に、そのファイルを共有オブジェクトとしてコンパイルします:
gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
次に、 LD_PRELOAD
を設定します プログラムと一緒にロードする変数:
$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
運が良ければ、問題は 不運 なしで解決されます 副作用。
LD_PRELOAD
を設定できます 必要に応じて、シェル内のライブラリを指定するか、システム全体でそのライブラリを指定することさえできます (絶対に NOT 推奨) /etc/ld.so.preload
.
ティーへの配管を検討しましたか?
./program | tee a.txt
ただし、「プログラム」が完了するまで標準出力に何も書き込まないと、tee でさえ機能しません。したがって、有効性はプログラムの動作に大きく依存します。
既存のプログラムの動作を変更しようとしている場合は、stdbuf (明らかにバージョン 7.5 以降の coreutils の一部) を試してください。
これは stdout を 1 行までバッファリングします:
stdbuf -oL command > output
これにより、stdout バッファリングが完全に無効になります:
stdbuf -o0 command > output