Python中实时拦截另一个进程的标准输出流

6
我希望能在Python脚本中运行系统进程,截取输出并实时逐行修改。以下是我最好的尝试,它会等待进程完成后再打印:
#!/usr/bin/env python
import subprocess

cmd = "waitsome.py"
proc = subprocess.Popen(cmd, shell=True, bufsize=256, stdout=subprocess.PIPE)
for line in proc.stdout:
    print ">>> " + line.rstrip()

脚本 waitsome.py 每 0.5 秒打印一行内容:
#!/usr/bin/env python
import time
from sys import stdout

print "Starting"
for i in range(0,20):
    time.sleep(0.5)
    print "Hello, iteration", i
    stdout.flush()

有没有一种简单的解决方案可以让subprocess允许实时迭代输出?我是否必须使用线程?

从前,我用Perl编写脚本,这很容易:

open(CMD, "waitsome.py |");
while (<CMD>) {
    print ">>> $_";
}
close(CMD);

重复问题:http://stackoverflow.com/search?q=%5Bpython%5D+subprocess+real-time,具体可以参考以下链接:https://dev59.com/UnRB5IYBdhLWcg3wv5o_,https://dev59.com/cXRA5IYBdhLWcg3w2xwI。 - S.Lott
抱歉,我只看到了第一个并且理解为 subprocess 的缓冲问题,而不是父 Python 脚本的问题。 - Seth Johnson
2个回答

15

在Python 2.*的所有实现中,遍历文件时不可避免地会以相当大的块进行缓冲--这是一个已知的问题。在Python 3.1中,它按您的意图工作,但最终的循环略有不同:

for line in proc.stdout:
    print(">>> " + str(line.rstrip()))

如果升级到 Python 3.1 不切实际(我知道通常是这样的!),那就换另一种方式,以老式的方式编写循环 - 循环的以下版本在 Python 2.* 中按照您的意愿工作:

while True:
    line = proc.stdout.readline()
    if not line:
        break
    print ">>> " + line.rstrip()

1
没错,但现在Python 3.1已经发布(整个I/O堆栈的实现要好得多),没有理由继续使用3.0(它肯定是一个过渡版本;-)。 - Alex Martelli
1
在Python 2中,可以使用for line in iter(proc.stdout.readline, ''): print ">>>", line,代替while循环。 - jfs

0

这整个过程可以封装在一个迭代器中:

def subprocess_readlines(out):
    while True:
        line = out.readline()
        if not line:
            return
        yield line

并被称为:

for line in subprocess_readlines(proc.stdout):
    print ">>>", line.rstrip()

Python中的缓冲机制自我提出问题以来已经发生了很大变化 :) 是的,对于Python 2.5而言需要付出很多努力才能实现的功能,现在只需要几行代码就可以完成。 - Seth Johnson
我不明白在这种方法中subprocess_readlines需要在哪里定义。你能详细说明一下吗? - 0-0
哎呀,我没有加上对 subprocess_readlines() 的调用。我刚刚更新了它。 - Jeff Younker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接