使用子进程获取实时输出
我正在尝试为一个命令行程序(svnadmin verify)写一个包装脚本,目的是在操作时显示一个漂亮的进度指示器。为了做到这一点,我需要能够立即看到被包装程序输出的每一行内容。
我想我可以使用 subprocess.Popen
来执行这个程序,设置 stdout=PIPE
,然后读取每一行输出并相应地处理它。但是,当我运行以下代码时,输出似乎被缓冲了,导致它分成了两部分显示:第一部分是第1行到第332行,第二部分是第333行到第439行(最后一行输出)。
from subprocess import Popen, PIPE, STDOUT
p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE,
stderr = STDOUT, shell = True)
for line in p.stdout:
print line.replace('\n', '')
在查看了一下关于subprocess的文档后,我发现了 bufsize
参数,所以我尝试将bufsize设置为1(逐行缓冲)和0(不缓冲),但这两个值似乎都没有改变输出的方式。
这时我开始有点无计可施,于是写了以下的输出循环:
while True:
try:
print p.stdout.next().replace('\n', '')
except StopIteration:
break
但结果还是一样。
使用subprocess执行的程序,能否获得“实时”的输出?在Python中有没有其他向前兼容的选项(不是 exec*
)?
24 个回答
31
你可以直接把子进程的输出导向不同的流。下面是一个简单的例子:
subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)
42
通过将缓冲区大小设置为1,你实际上是在强制程序不对输出进行缓冲。
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
print line,
p.stdout.close()
p.wait()
94
我试过这个,出于某种原因,虽然这段代码
for line in p.stdout:
...
在缓存方面表现得很积极,但这个变体
while True:
line = p.stdout.readline()
if not line: break
...
却没有。显然这是一个已知的bug:http://bugs.python.org/issue3907(这个问题在2018年8月29日已经“关闭”了)