看着一些东西被写到一个带有尾巴的文件中

int*_*tar 26 linux redirection command-line stdout

我有一个 python 程序,它正在慢慢地生成一些输出。

我想在一个文件中捕获它,但我也认为我可以用尾巴观看它。

所以在一个终端中我正在做:

python myprog.py > output.txt
Run Code Online (Sandbox Code Playgroud)

并在另一个终端:

tail -f output.txt
Run Code Online (Sandbox Code Playgroud)

但是在 python 程序运行时,尾巴似乎没有向我显示任何东西。

如果我按 ctrl-c 来杀死 python 脚本,突然尾部output.txt开始填满。但不是在 python 运行时。

我究竟做错了什么?

小智 44

您可能还需要显式刷新缓冲区以使其在生成时通过管道传输。这是因为通常只在管道缓冲区填满(我相信以千字节为单位)以及 stdin 消息结束时才打印输出。这可能是为了节省读/写。您可以在每次打印后执行此操作,或者如果您正在循环,则在循环内的最后一次打印之后执行此操作。

import sys
...
print('Some message')
sys.stdout.flush()
Run Code Online (Sandbox Code Playgroud)

  • 你也可以使用 `print` 的 `flush` 参数来做同样的事情。例如,`print('some message', flush=True)`。 (12认同)
  • 它与管道的缓冲区无关,而是与 `stdout` 机制有关,如果它不写入 tty,则不会在换行后刷新。 (12认同)
  • 如果您已经阅读到这里,请不要考虑关闭并重新打开文件来执行此操作,搜索将是一个问题,尤其是对于非常大的文件。(我已经看到这样做了!)。 (8认同)
  • @wizzwizz4,唯一不同的是,Python 遵循标准 C 库的约定,默认情况下,将 stderr 配置为无缓冲,将 stdout 配置为在指向 TTY 时进行行缓冲,但如果将 stdout 打开为非缓冲,则将其配置为无缓冲-TTY 设备。 (2认同)

小智 34

使用无缓冲标志运行 python:

python -u myprog.py > output.txt
Run Code Online (Sandbox Code Playgroud)

然后将实时打印输出。

  • 这是正确答案。默认情况下,Python 在写入控制台时写入无缓冲(或实际上为文本 I/O 缓冲),但在 stdout 重定向到文件时缓冲。-u 强制 Python 在写入时无缓冲(或对文本进行行缓冲)。 (4认同)

n8t*_*8te 19

与其尝试拖尾实时文件,不如使用tee。它被用来做你想做的事情。

来自男士 T 恤

tee(1) - Linux 手册页

名称 tee - 从标准输入读取并写入标准输出和文件

概要

tee [OPTION]... [FILE]...
Run Code Online (Sandbox Code Playgroud)

描述

将标准输入复制到每个文件,也复制到标准输出。

-a, --append  
   append to the given FILEs, do not overwrite  
-i, --ignore-interrupts  
   ignore interrupt signals   
--help  
   display this help and exit  
--version
   output version information and exit
Run Code Online (Sandbox Code Playgroud)

如果 FILE 是 -,则再次复制到标准输出。

所以在你的情况下,你会运行:

python myprog.py | tee output.txt
Run Code Online (Sandbox Code Playgroud)

编辑:正如其他人所指出的,这个答案将遇到 OP 最初遇到的相同问题,除非sys.stdout.flush()在 Davey 接受的答案中描述的 python 程序中使用。我在发布此答案之前所做的测试并未准确反映 OP 的用例。

tee 仍然可以用作替代方法——尽管不是最佳的——在写入文件的同时显示输出的方法,但戴维的答案显然是正确和最好的答案。

  • 这需要一个永久的控制台会话,这就是为什么使用 `tail -F` 通常更容易,或者甚至更好地使用 `less` 的跟随功能。但在所有情况下都应该使用“flush”。 (11认同)
  • 这不会解决 OP 遇到的问题。Python 到管道的输出将被缓冲,就像输出到文件一样。 (8认同)

Pet*_*des 9

术语:在这种情况下,任何地方都没有管道。(我编辑了问题来解决这个问题)。管道是一种不同类型的文件(内核内部的缓冲区)。

这是重定向到常规文件。

C stdio 和 Python 在连接到 TTY 时默认使 stdout 为行缓冲,否则为全缓冲。行缓冲意味着缓冲区在换行后刷新。全缓冲意味着它只有write()在它已满时才被刷新以对操作系统可见(即通过系统调用)。

您最终会看到输出,一次可能是 4kiB 的块。(我不知道默认缓冲区大小。)这通常更有效,并且意味着对实际磁盘的写入更少。但对于交互式监控来说不是很好,因为输出隐藏在写入过程的内存中,直到它被刷新。

在 Stack Overflow 上,有一个禁用输出缓冲Python 问答,其中列出了许多在 Python 中将无缓冲(或行缓冲?)输出到标准输出的方法。问题本身总结了答案。

选项包括运行python -u(或者我猜放在#!/usr/bin/python -u脚本的顶部),或使用该PYTHONUNBUFFERED程序的环境变量。或者在某些/所有print功能之后显式刷新,就像@Davey 的回答所暗示的那样。


其他一些程序有类似的选项,例如 GNU grep--line-buffered和 GNUsed-u/ --unbuffered,用于这样的用例,或者例如管道你的 python 程序的输出。例如./slowly-output-stuff | grep --line-buffered 'foo.*bar'