使用Python强制另一个程序的标准输出无缓冲

14 投票
7 回答
5664 浏览
提问于 2025-04-15 14:57

一个Python脚本正在控制一个在Linux上运行的外部应用程序,它通过管道将输入传递给外部应用程序的标准输入(stdin),并通过管道从外部应用程序的标准输出(stdout)读取输出。

问题是,写入管道的数据是按块缓冲的,而不是按行缓冲的,因此在控制脚本接收到外部应用程序输出的数据(比如通过printf输出的内容)之前,会出现延迟。

而且,外部应用程序无法修改,不能添加明确的fflush(0)调用来解决这个问题。

那么,如何使用Python标准库中的pty模块和subprocess模块来解决这个问题呢?

7 个回答

3

我觉得这不太可能。如果源应用程序不清空它的输出缓冲区,数据就不会从这个程序中流出,直到缓冲区满了,才会被迫清空。

注意像file这样的成熟命令有一个选项(-n),可以明确地让它清空输出。这在使用file命令从管道读取输入文件名并打印检测到的类型时是必须的。因为在这种模式下,file程序完成后不会退出,否则输出就不会显示出来。

从更底层的角度来看,输出缓冲的意思是,当你在一个缓冲流上使用write()时,数据会被复制到内存中的一个缓冲区,直到这个缓冲区满了,或者(通常情况下)直到遇到换行符。然后,缓冲区中直到溢出或换行符的部分会通过write()写入到底层的系统文件描述符(这可以是一个文件、一个管道、一个套接字等)。

我不明白你打算怎么从外部让那个程序清空它的缓冲区。

5

这样做是可能的,但我想到的唯一解决方案比较复杂,不太容易移植,而且可能会有很多问题。你可以使用LD_PRELOAD这个功能,让外部应用程序加载一个动态库,这个库里有一个构造函数,它会调用setvbuf来取消stdout的缓冲。你可能还需要在这个库里包裹一下setvbuf,以防应用程序自己把stdout设置成缓冲模式。此外,你还需要包裹一下fwrite和printf,这样每次调用时它们都会立即刷新输出。写这个要预加载的.so文件会让你脱离Python的环境。

6

你可以通过使用伪终端(PTY)来解决这个问题,具体步骤如下:

  • 创建一个伪终端的主从配对;
  • 把子进程的输入、输出和错误信息连接到伪终端的从设备;
  • 在父进程中从伪终端的主设备读取和写入数据。

撰写回答