Python:从subprocess.communicate()读取stream式input

我正在使用Python的subprocess.communicate()从运行了大约一分钟的进程中读取stdout。

如何以stream方式打印出该进程stdout的每一行,以便我可以在输出生成时看到输出,但在继续之前仍然阻止进程终止?

subprocess.communicate()似乎给所有的输出一次。

请注意,我认为JF塞巴斯蒂安的方法(下)比较好。


这里是一个简单的例子(没有检查错误):

 import subprocess proc = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, ) while proc.poll() is None: output = proc.stdout.readline() print output, 

如果ls结束得太快,那么在读取所有数据之前while循环可能会结束。

你可以通过这种方式在标准输出中捕获其余部分:

 output = proc.communicate()[0] print output, 

一旦子stream程刷新其stdout缓冲区,就可以逐行获取子stream程输出:

 #!/usr/bin/env python2 from subprocess import Popen, PIPE p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1) with p.stdout: for line in iter(p.stdout.readline, b''): print line, p.wait() # wait for the subprocess to exit 

iter()用于在写入行以解决Python 2中的预读错误时立即读取行。

如果subprocess的stdout在非交互模式下使用块缓冲而不是行缓冲(这会导致输出延迟,直到孩子的缓冲区已满或由孩子明确刷新),那么您可以尝试强制使用未缓冲的输出pexpectpty模块或unbufferstdbufscript实用程序 ,请参阅Q:为什么不使用pipe道(popen())?


这是Python 3代码:

 #!/usr/bin/env python3 from subprocess import Popen, PIPE with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1, universal_newlines=True) as p: for line in p.stdout: print(line, end='') 

注意:不像Python 2那样输出subprocess的字节串; Python 3使用文本模式(cmd的输出使用locale.getpreferredencoding(False)编码进行解码)。

我相信以stream媒体的方式从stream程中收集输出的最简单的方法是这样的:

 import sys from subprocess import * proc = Popen('ls', shell=True, stdout=PIPE) while True: data = proc.stdout.readline() # Alternatively proc.stdout.read(1024) if len(data) == 0: break sys.stdout.write(data) # sys.stdout.buffer.write(data) on Python 3.x 

readline()read()函数应该只在EOF结束时返回一个空string,否则会在无法读取时阻塞( readline()包含换行符,所以在空行上返回“的\ n“)。 这避免了在循环之后需要一个尴尬的最后的communicate()调用。

对于具有很长行的文件, read()可能更适用于减less最大内存使用量 – 传递给它的数字是任意的,但排除它会导致一次读取整个pipe道输出,这可能是不希望的。

如果你想要一个非阻塞的方法,不要使用process.communicate() 。 如果将subprocess.Popen()参数stdoutPIPE ,则可以从process.stdout读取并检查进程是否仍然使用process.poll()运行。

如果您只是试图通过实时传递输出,则很难做到比这更简单:

 import subprocess # This will raise a CalledProcessError if the program return a nonzero code. # You can use call() instead if you don't care about that case. subprocess.check_call(['ls', '-l']) 

请参阅subprocess.check_call()的文档 。

如果你需要处理输出,当然,循环它。 但是,如果你不这样做,只是保持简单。

编辑: JF Sebastian指出stdout和stderr参数的默认值传递给sys.stdout和sys.stderr,并且如果sys.stdout和sys.stderr被replace(比如捕获输出testing)。

 myCommand="ls -l" cmd=myCommand.split() # "universal newline support" This will cause to interpret \n, \r\n and \r equally, each as a newline. p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True) while True: print(p.stderr.readline().rstrip('\r\n'))