在运行时拦截子进程的标准输出

Posted

技术标签:

【中文标题】在运行时拦截子进程的标准输出【英文标题】:Intercepting stdout of a subprocess while it is running 【发布时间】:2010-10-06 08:18:33 【问题描述】:

如果这是我的子流程:

import time, sys
for i in range(200):
    sys.stdout.write( 'reading %i\n'%i )
    time.sleep(.02)

这是控制和修改子进程输出的脚本:

import subprocess, time, sys

print 'starting'
    
proc = subprocess.Popen(
    'c:/test_apps/testcr.py',
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE  )

print 'process created'

while True:
    #next_line = proc.communicate()[0]
    next_line = proc.stdout.readline()
    if next_line == '' and proc.poll() != None:
        break
    sys.stdout.write(next_line)
    sys.stdout.flush()
    
print 'done'

为什么readlinecommunicate 一直等到进程完成运行?有没有一种简单的方法来实时传递(和修改)子进程的标准输出?

我使用的是 Windows XP。

【问题讨论】:

相关:How to flush output of Python print? 【参考方案1】:

正如查尔斯已经提到的,问题在于缓冲。我在为 SNMPd 编写一些模块时遇到了类似的问题,并通过将 stdout 替换为自动刷新版本来解决它。

我使用了以下代码,灵感来自 ActiveState 上的一些帖子:

class FlushFile(object):
    """Write-only flushing wrapper for file-type objects."""
    def __init__(self, f):
        self.f = f
    def write(self, x):
        self.f.write(x)
        self.f.flush()

# Replace stdout with an automatically flushing version
sys.stdout = FlushFile(sys.__stdout__)

【讨论】:

我看不出这与在每个 sys.stdout.readline() 之后调用 sys.stdout.flush() 有何不同,这就是我所做的。我还尝试为子进程设置 bufsize=0。 子进程需要flush,而不是父进程。 是的,在这个例子中子进程也是一个python脚本。所以替换子进程中的stdout。在父进程中调用 sys.stdout.flush() 没有任何作用。 好的。我看到我在那里做了什么。当然,这个子进程只是一个示例。我的真实过程是一大块已编译的 FORTRAN,我无法访问它的源代码。在这种情况下,我只需要希望孩子没有缓冲输出?那么 subprocess.Popen 的 bufsize 是做什么的呢? 据我所知,决定输出缓冲区大小的是应用程序代码。我不认为你可以在外部做任何事情,除非它是动态链接的并且你预加载了一个替换系统调用的库。但这是一个巨大的黑客,超出了这个问题的范围:)【参考方案2】:

进程输出被缓冲。在更多的 UNIXy 操作系统(或 Cygwin)上,pexpect 模块可用,它背诵了所有必要的咒语以避免与缓冲相关的问题。但是,这些咒语需要有效的pty module,这在本机(非 cygwin)win32 Python 版本上不可用。

在您控制子进程的示例情况下,您可以让它在必要时调用sys.stdout.flush() - 但对于任意子进程,该选项不可用。

另请参阅 pexpect 常见问题解答中的 the question "Why not just use a pipe (popen())?"。

【讨论】:

以上是关于在运行时拦截子进程的标准输出的主要内容,如果未能解决你的问题,请参考以下文章

Python 运行守护程序子进程并读取标准输出

Jupyter notebook 中 Python 子进程的实时标准输出输出

检查 python 中正在运行的子进程的标准输出

将标准输出子进程转换为 Ngrok 的变量

使用文件作为子进程的标准输入和标准输出

使用 BOOST 进程在单独的线程中读取子进程标准输出