立即将Python标准输出写入文件


51

尝试将标准输出从Python脚本写入文本文件(python script.py > log)时,将在启动命令时创建该文本文件,但直到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

Answers:


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
谢谢,我对缓冲一无所知!知道了这一点,Google很快就告诉我python -u script.py做到了。编辑这么多的答案一次,我接受了您的回答,因为它指出了缓冲的方向。
巴特

1
@julbra很酷,是的,我也不知道python是否具有该选项。一些命令行程序也具有类似的选项-例如,--line-buffered用于grep,但是其他一些则没有。 stdbuf是通用的处理所有杂项的工具。
Digital Trauma

@DigitalTrauma:stdbuf -o0 python script.py > log在这种确定的情况下,根本不使用任何缓冲不是更好吗?
heemayl

@heemayl -oL是一个折衷方案。通常,较大的缓冲区在重定向到某处时会提供更好的性能(更少的系统调用和更少的I / O操作)。但是,如果绝对有必要在输出每个字符时看到它们,-o0那么将是必需的。
Digital Trauma 2015年

@Paul请避免在答案之间复制粘贴内容,或者至少要提及提供该内容的原始作者。
2015年

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这种多余的论点就行不通了,因此,一个人可以PYTHONUNBUFFERED=1 ./my_scriipt.py > output.txt分两步运行或执行它:

$ export PYTHONUNBUFFERED=1
$ ./myscript.py

10

您应该传递flush=Trueprint函数:

import time

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

根据文档,默认情况下,print不执行任何有关刷新的操作:

通常是否由文件确定输出是否被缓冲,但是如果 flush关键字参数为true,则将强制刷新流。

sys的strems 文档说:

交互式时,标准流是行缓冲的。否则,它们像常规文本文件一样被块缓冲。您可以使用-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 2015年

4
@dotancohen实际上,有关该部分的内容print(flush=True)是由第三方作者我的回答添加到该答案的。我发现从我的答案中撕掉内容,然后不加信用地将它们放到另一个中,这是一种不好的口味。我决定只添加答案,因为没有答案提供了在较新版本的python中实现OP所需功能的最简单方法,并且我添加了“旧方法”只是为了完整性。下次,请先查看修订历史记录,然后再发表评论和/或拒绝投票。
2015年

@Bakuriu:不好意思!这显示了在投票时总是发表原因的充分原因。您能否编辑一下帖子,以便我可以将我的下投票更改为上投票?谢谢!
dotancohen 2015年

如果您__future__导入:,它应该与Python 2.7一起使用from __future__ import print_function。但是,是的,这仅是为了与Python 3兼容
Sergiy Kolodyazhnyy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.