在Python中实时拦截另一个进程的stdout

6 投票
2 回答
9403 浏览
提问于 2025-04-15 12:42

我想在一个系统进程中运行,实时捕捉输出,并逐行修改它,这个过程是在一个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 每半秒打印一行:

#!/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);

2 个回答

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()
16

在处理文件时,循环读取文件内容会不可避免地将数据分成比较大的块来缓存,这在所有的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()

撰写回答