Python stdoutをすぐにファイルに書き込む


51

Pythonスクリプトからテキストファイル(python script.py > log)にstdoutを書き込もうとすると、コマンドの開始時にテキストファイルが作成されますが、Pythonスクリプトが終了するまで実際のコンテンツは書き込まれません。例えば:

script.py:

import time
for i in range(10):
    print('bla')
    time.sleep(5)

で呼び出されpython script.pyた場合、5秒ごとにstdoutに出力されますが、を呼び出した場合python script.py > log、スクリプトが終了するまでログファイルのサイズはゼロのままです。スクリプトの進行状況を追跡できるように、ログファイルに直接書き込むことは可能tailですか(例:を使用)?

編集それpython -u script.pyはトリックを行うことが判明し、stdoutのバッファリングについては知りませんでした。


1
@jezmck、質問の間違いを理解できたかもしれません。
zyxue

回答:


64

これは通常、プロセスSTDOUTが端末以外にリダイレクトされると、出力がOS固有のサイズのバッファー(多くの場合4kまたは8k)にバッファーされるために発生します。逆に、端末に出力する場合、STDOUTは行バッファリングされるか、まったくバッファリングされない\nため、各文字の後または各文字の出力が表示されます。

通常、stdbufユーティリティを使用してSTDOUTバッファリングを変更できます。

stdbuf -oL python script.py > log

の場合tail -F log、生成された各行の出力がすぐに表示されます。


または、各印刷後に出力ストリームを明示的にフラッシュすることで、同じ結果が得られます。sys.stdout.flush()Pythonでこれを達成するようです。Python 3.3以降を使用している場合、print関数にflushはこれを行うキーワードもありますprint('hello', flush=True)


8
おかげで、私はバッファリングについて知りませんでした!それを知って、グーグルはすぐにそれpython -u script.pyがトリックだと言った。編集一度にたくさんの答えがありましたが、バッファリングの方向を教えてくれたので、私はあなたの答えを受け入れました。
バート

1
@julbra Cool、はい、Pythonにもそのオプションがあることは知りませんでした。一部のコマンドラインプログラムにも同様のオプションがあります。たとえば--line-bufferedforですがgrep、そうでないものもあります。 stdbufは、そうでないものを扱うための一般的なキャッチオールユーティリティです。
デジタル外傷

@DigitalTrauma:バッファリングをまったく使用しないほうがいいのではないstdbuf -o0 python script.py > logでしょうか。つまり、この種の決まった状況ではどうでしょうか。
heemayl

@heemayl -oLは妥協です。一般に、バッファを大きくすると、どこかにリダイレクトするときにパフォーマンスが向上します(システムコールが少なくなり、I / O操作が少なくなります)。ただし、出力時に各文字を表示することが絶対に必要な場合は、はい-o0が必要になります。
デジタル外傷

@Paul回答間のコンテンツのコピーペーストを避けるか、少なくともコンテンツを提供した元の著者に言及してください。
バクリウ

44

これは仕事をするはずです:

import time, sys
for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)

Pythonはstdoutデフォルトでバッファリングするため、ここでsys.stdout.flush()はバッファをフラッシュするために使用しました。

別の解決策は、の-u(バッファなし)スイッチを使用することですpython。したがって、次のことも行います。

python -u script.py >> log

11

バッファなしの出力にpython独自のオプションを使用するというテーマのバリエーションは、#!/usr/bin/python -u最初の行として使用することです。

#!/usr/bin/env python、余分な引数ではありませんよ仕事なので、代わりに、1は実行できPYTHONUNBUFFERED=1 ./my_scriipt.py > output.txtまたは2つの段階でそれを実行します。

$ export PYTHONUNBUFFERED=1
$ ./myscript.py

10

関数に渡す必要がflush=Trueありprintます。

import time

for i in range(10):
    print('bla', flush=True)
    time.sleep(5)

ドキュメントによると、デフォルトでprintは、フラッシュについて何も強制しません:

通常、出力がバッファリングされるかどうかはファイルによって決まりますが、 flushキーワード引数がtrueの場合、ストリームは強制的にフラッシュされます。

また、sysのストームのドキュメントには次のように書かれています。

対話型の場合、標準ストリームはラインバッファリングされます。それ以外の場合は、通常のテキストファイルのようにブロックバッファリングされます。この値は、-uコマンドラインオプションでオーバーライドできます。


旧バージョンのpythonにこだわっている場合flushは、sys.stdoutストリームのメソッドを呼び出す必要があります。

import sys
import time

for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)

1
flush = True引数はPython 3.4.2でうまく機能しますが、実際には古代(..)Python 2.7.9
Bart

この回答は、DigitalTrauma10時間前に言ったことと同じことを示唆しています。同じ投稿を再度投稿するのではなく、彼の投稿に賛成する必要があります。
-dotancohen

4
@dotancohen実際には、第三者の作者が私のprint(flush=True)答えのにその部分に追加しました。私の答えから内容をリッピングして、信用なしで他のものに入れるのは悪い味だと思います。私は私の答えを追加することを決めただけで何の答えはOPのpythonの新しいバージョンに何を望むか達成するための最も簡単な方法のいずれかの言及はありませんので、私はちょうど完全を期すため、「古いやり方」を追加しました。次回はコメントやダウンボーティングの前に改訂履歴を確認してください。
バクリウ

@バクリウ:申し訳ありません!これはなぜ投票するかを常に投稿する正当な理由を示しています。投稿を少し編集して、下票を上票に変更してもらえますか?ありがとうございました!
-dotancohen

__future__インポートする場合、Python 2.7で動作するはずですfrom __future__ import print_function。しかし、はい、Pythonの3のみとの互換性を確保するためだ
Sergiy Kolodyazhnyy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.