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在非交互模式下使用块缓冲而不是行缓冲(这会导致输出延迟,直到孩子的缓冲区已满或由孩子明确刷新),那么您可以尝试强制使用未缓冲的输出pexpect
, pty
模块或unbuffer
, stdbuf
, script
实用程序 ,请参阅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()
参数stdout
为PIPE
,则可以从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'))