通过stdout和PIPE的实时subprocess.Popen
我正在尝试从一个 subprocess.Popen
调用中获取 stdout
(标准输出),虽然我通过以下方法很容易实现:
cmd = subprocess.Popen('ls -l', shell=True, stdout=PIPE)
for line in cmd.stdout.readlines():
print line
但是我希望能够实时获取 stdout
。因为上面的方法是等到所有的 stdout
都抓取完后才返回。
所以对于记录日志来说,这并不能满足我的需求(比如,我想在事情发生的时候就能“看到”发生了什么)。
有没有办法在运行时逐行获取 stdout
?还是说这是 subprocess
的一个限制(必须等到 PIPE
关闭后才能获取)?
编辑:如果我把 readlines()
换成 readline()
,我只会得到 stdout
的最后一行(这并不好):
In [75]: cmd = Popen('ls -l', shell=True, stdout=PIPE)
In [76]: for i in cmd.stdout.readline(): print i
....:
t
o
t
a
l
1
0
4
8 个回答
19
其实,真正的解决办法是直接把子进程的输出(stdout)重定向到你当前进程的输出。
实际上,按照你现在的方法,你只能同时打印标准输出(stdout),而不能打印错误输出(stderr)。
import sys
from subprocess import Popen
Popen("./slow_cmd_output.sh", stdout=sys.stdout, stderr=sys.stderr).communicate()
使用communicate()
这个方法是为了让调用在子进程结束之前一直阻塞,也就是说,它会等到子进程完成后再继续执行下一行代码。否则,程序可能会在子进程还没结束的时候就提前结束了(不过,即使你的Python脚本关闭了,重定向到你的标准输出仍然有效,我测试过)。
这样一来,你就可以同时重定向标准输出和错误输出,而且是实时的。
比如,在我的测试中,我用这个脚本slow_cmd_output.sh
进行了测试:
#!/bin/bash
for i in 1 2 3 4 5 6; do sleep 5 && echo "${i}th output" && echo "err output num ${i}" >&2; done
23
你的解释器在缓存数据。可以在打印语句后面加上一个调用 sys.stdout.flush() 的命令。