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

Bar*_*art 73 shell python stdout

尝试将 Python 脚本中的 stdout 写入文本文件 ( python script.py > log) 时,会在命令启动时创建文本文件,但在 Python 脚本完成之前不会写入实际内容。例如:

脚本.py:

import time
for i in range(10):
    print('bla')
    time.sleep(5)
Run Code Online (Sandbox Code Playgroud)

使用 调用时每 5 秒打印到标准输出python script.py,但是当我调用 时python script.py > log,日志文件的大小保持为零,直到脚本完成。是否可以直接写入日志文件,以便您可以跟踪脚本的进度(例如使用tail)?

编辑事实证明,这样python -u script.py做的技巧,我不知道标准输出的缓冲。

Dig*_*uma 81

发生这种情况是因为通常当进程 STDOUT 被重定向到终端以外的东西时,输出会被缓冲到一些操作系统特定大小的缓冲区(在许多情况下可能是 4k 或 8k)。相反,当输出到终端时,STDOUT 将被行缓冲或根本不缓冲,因此您会在\n每个字符或每个字符之后看到输出。

您通常可以使用该stdbuf实用程序更改 STDOUT 缓冲:

stdbuf -oL python script.py > log
Run Code Online (Sandbox Code Playgroud)

现在,如果您tail -F log,您应该在生成时立即看到每一行输出。


或者,每次打印后显式刷新输出流应该达到相同的效果。看起来sys.stdout.flush()应该在 Python 中实现这一点。如果您使用的是 Python 3.3 或更新版本,则该print函数还有一个flush关键字可以执行此操作:print('hello', flush=True).

  • 谢谢,我不知道缓冲!知道这一点后,Google 很快就告诉我`python -u script.py` 可以解决问题。*编辑*一下子有这么多答案,我接受了你的答案,因为它为我指明了缓冲的方向。 (9认同)
  • @julbra 酷,是的,我不知道 python 也有那个选项。一些命令行程序也有类似的选项——例如`grep` 的`--line-buffered`,但其他一些没有。`stdbuf` 是处理那些没有的通用工具。 (2认同)

hee*_*ayl 55

这应该可以完成这项工作:

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

由于stdout默认情况下Python 会缓冲,这里我已经用来sys.stdout.flush()刷新缓冲区。

另一解决方案是使用-u的(未缓冲的)开关python。因此,以下内容也将执行:

python -u script.py >> log
Run Code Online (Sandbox Code Playgroud)


Ser*_*nyy 19

使用 python 自己的无缓冲输出选项的主题的变化是#!/usr/bin/python -u用作第一行。

由于#!/usr/bin/env python这个额外的参数不起作用,所以或者,可以PYTHONUNBUFFERED=1 ./my_scriipt.py > output.txt分两步运行或完成:

$ export PYTHONUNBUFFERED=1
$ ./myscript.py
Run Code Online (Sandbox Code Playgroud)


Bak*_*riu 14

你应该传递flush=Trueprint函数:

import time

for i in range(10):
    print('bla', flush=True)
    time.sleep(5)
Run Code Online (Sandbox Code Playgroud)

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

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

sys的 strems文档说:

交互时,标准流是行缓冲的。否则,它们像常规文本文件一样被块缓冲。您可以使用-u命令行选项覆盖此值。


如果您坚持使用古老版本的python,则必须调用流的flush方法sys.stdout

import sys
import time

for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)
Run Code Online (Sandbox Code Playgroud)

  • @dotancohen 实际上,关于`print(flush=True)` 的部分是由第三方作者*在*我的答案之后添加的。我发现从我的答案中撕下内容并将它们放在另一个没有信用的地方是不好的。我决定*仅*添加我的答案,因为没有任何答案提到在较新版本的python中实现OP想要的最简单的方法,我添加了“旧方法”只是为了完整性。下次请在评论和/或否决之前检查修订历史。 (4认同)