2010-04-26 187 views
55

我正在使用Python的subprocess.communicate()从运行大约一分钟的进程读取stdout。Python:读取来自subprocess.communicate()的流输入

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

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

+0

相关:[获取实时输出使用子过程](http://stackoverflow.com/q/803265/4279) – jfs 2014-10-16 20:11:54

回答

36

请注意,我认为J.F. Sebastian's method (below)更好。


下面是一个简单的例子(没有进行错误检查):

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, 
+1

该方案是否成为python doc引用的缓冲区阻塞问题的受害者? – 2010-04-26 19:22:08

+0

@海因里希,缓冲区阻塞问题不是我理解得很好。我相信(仅仅从google搜索),只有当你没有从while循环中读取stdout(和stderr?)时才会出现这个问题。所以我认为上面的代码是可以的,但我不能肯定地说。 – unutbu 2010-04-26 19:44:44

+1

这实际上也从一个阻塞问题的困扰,几年前我没有结束麻烦,其中的readline将阻止,直到它得到了即使PROC结束了一个换行符。我不记得了解决方案,但我认为它有事情做与做读取一个工作线程,只是循环'而proc.poll()是无:time.sleep(0)'或诸如此类的话。基本上,您需要确保输出换行符是流程所做的最后一件事(因为您不能让解释器时间再次循环),或者您需要做一些“花哨”的事情。 – 2010-04-26 20:05:32

3

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

+1

[非阻塞方法不是直接的(http://stackoverflow.com/q/375427/4279) – jfs 2015-09-23 14:45:34

6

我相信从以流方式的过程收集输出最简单的方法是这样的:

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结束后返回一个空字符串,否则它将在没有任何内容需要读取时阻塞(readline()包含换行符,所以在空行上,它返回“\ n”)。这避免了在循环之后需要一个尴尬的最终communicate()调用。

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

+1

'data = proc.stdout.read()'阻塞,直到读取所有*数据。你可能会把它与os.read(fd,maxsize)混淆起来,它可以提前返回(只要有数据可用)。 – jfs 2013-08-22 09:15:24

+0

你是对的,我错了。但是,如果将合理数量的字节作为参数传递给'read()',那么它可以正常工作,并且只要最大行长度合理,'readline()'也可以正常工作。相应地更新我的答案。 – 2013-08-22 23:46:34

99

尽快子进程刷新其标准输出缓冲区获得由线子输出线:

#!/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()来,尽快为他们写要解决the read-ahead bug in Python 2读线。

如果子进程的stdout在非交互模式下使用块缓冲而不是行缓冲(导致输出延迟,直到孩子的缓冲区已满或被孩子明确刷新),那么您可以尝试强制使用pexpect, pty modulesunbuffer, stdbuf, script utilities无缓冲输出,见Q: Why not just use a 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字节串原样; Python 3使用文本模式(cmd的输出使用locale.getpreferredencoding(False)编码进行解码)。

+0

b“是什么意思? – Aaron 2014-04-07 18:58:20

+3

'b'''是Python 2.7和Python 3中的'bytes'文字。 – jfs 2014-04-07 18:59:03

+0

'bufsize = 1'是关键!至少它在cmd是'fastboot'时起作用。 – 2014-08-03 20:50:14

2

如果你只是想通过实时传递输出,很难得到比这更简单:

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']) 

docs for subprocess.check_call()

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

编辑:J.F. Sebastian指出二者,对于输出和错误参数的缺省值通过sys.stdout来和sys.stderr,那如果sys.stdout的和sys.stderr已被替换,这将失败(比如,用于在测试中捕获输出)。

+0

如果将'sys.stdout'或'sys.stderr'替换为没有真正的fileno()的文件类对象,它将不起作用。如果'sys.stdout','sys.stderr'没有被替换,那么它更简单:'subprocess.check_call(args)'。 – jfs 2015-09-22 18:47:24

+0

谢谢!我意识到替换sys.stdout/stderr的变幻莫测,但不知何故,如果你忽略参数,它会将stdout和stderr传递给正确的地方。除非我想要'CalledProcessError',否则我喜欢'call()'通过'check_call()'。 – Nate 2015-09-23 14:41:29

+0

'python -mthis':*“错误不应该默默通过。” 除非明确声明为无效。“*这就是为什么_example code_应该比'call()'更适合'check_call()'的原因。 – jfs 2015-09-23 14:42:43

0
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')) 
+1

解释你的解决方案只是为了让人们更好地理解,总是很好 – DaFois 2017-11-12 23:44:50