使用Python subprocess.call实时打印文本时的问题
首先,我要导入这些东西:
import os, shutil
从 subprocess 导入 call, PIPE, STDOUT
我有一行代码是用来调用 bjam 来编译一个库的:
call(['./bjam',
'-j8',
'--prefix="' + tools_dir + '"'],
stdout=PIPE)
我希望在编译的时候能实时打印出文本。但实际上,它是在最后才把所有内容一起打印出来。
当我这样运行时,它什么都不打印。我尝试在 Python 之外运行这个命令,发现所有的输出都是在标准输出(stdout)里(当我执行 ./bjam -j8 > /dev/null
时没有任何输出,而当我执行 ./bjam -j8 2> /dev/null
时却有输出)。
我到底哪里做错了?我想要实时打印 call 的输出。
顺便提一下,我在输出 git clone 操作的结果时也注意到了一些事情:
call(['git',
'clone', 'https://github.com/moses-smt/mosesdecoder.git'],
stdout=PIPE)
这个命令在调用过程中会实时打印标准输出的文本。
call(['git',
'clone', 'https://github.com/moses-smt/mosesdecoder.git'],
stdout=PIPE, stderr=STDOUT)
而这个命令却没有打印任何文本。这是怎么回事呢?
3 个回答
call
这个函数不会打印它捕获到的任何内容。正如文档中所说:“不要在这个函数中使用 stdout=PIPE 或 stderr=PIPE。因为在当前进程中没有读取这些管道,如果子进程产生的输出太多,可能会填满操作系统的管道缓冲区,从而导致子进程被阻塞。”
可以考虑使用 check_output
,并打印它的返回值。
在第一个使用 git 的例子中,你没有捕获 stderr
,所以它的输出通常会直接显示在你的终端上。
stdout=PIPE
是用来把子进程的输出(也就是它打印的内容)重定向到一个管道里。你只有在想要在代码中读取这个子进程的输出时,才需要这样做,比如使用 proc.communicate()
方法或者直接用 proc.stdout
属性。
如果你去掉这个设置,子进程就会像在命令行中那样直接把输出打印出来:
from subprocess import check_call
check_call(['./bjam', '-j8', '--prefix', tools_dir])
我使用了 check_call()
方法,这样如果子进程出错,就会抛出一个异常。
如果你想要逐行读取子进程的输出(并且把每一行作为变量在 Python 中使用),可以参考这个链接:Python: read streaming input from subprocess.communicate(),这样一有输出就能立刻获取。
尝试一下:
def run(command):
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
for lineno, line in enumerate(proc.stdout):
try:
print(line.decode('utf-8').replace('\n', ''))
except UnicodeDecodeError:
print('error(%d): cannot decode %s' % (lineno, line))
try...except
这个逻辑是针对 Python 3 的(可能是 3.2 或 3.3,我不太确定),因为这里的 line
是一个字节数组,而不是字符串。对于早期版本的 Python,你应该可以这样做:
def run(command):
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
for line in proc.stdout:
print(line.replace('\n', ''))
现在,你可以这样做:
run(['./bjam', '-j8', '--prefix="' + tools_dir + '"'])