使用subprocess获取实时输出

我正在尝试编写一个命令行程序的包装脚本(svnadminvalidation),将显示一个很好的进度指示器的操作。 这要求我能够在输出包装程序后立即看到每行输出。

我想我只是使用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', '') 

在稍微查看Popen的文档后,我发现了bufsize参数,所以我尝试将bufsize设置为1(缓冲每行)和0(无缓冲),但是这两个值都没有改变线路传送的方式。

在这一点上,我开始抓住吸pipe,所以我写了下面的输出循环:

 while True: try: print p.stdout.next().replace('\n', '') except StopIteration: break 

但得到了同样的结果。

是否有可能获得使用subprocess执行的程序的“实时”程序输出? Python中是否有其他的选项是向前兼容的(而不是exec* )?

我试过这个,出于某种原因,而代码

 for line in p.stdout: ... 

积极缓冲,变种

 while True: line = p.stdout.readline() if not line: break ... 

才不是。 显然这是一个已知的bug: http : //bugs.python.org/issue3907

 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1) for line in iter(p.stdout.readline, b''): print line, p.stdout.close() p.wait() 

你可以试试这个:

 import subprocess import sys process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) while True: out = process.stdout.read(1) if out == '' and process.poll() != None: break if out != '': sys.stdout.write(out) sys.stdout.flush() 

如果使用readline而不是read,则会出现input消息未打印的情况。 尝试一个命令需要一个内联input,看看你自己。

我又碰到了同样的问题。 我的解决方法是迭代read方法,即使您的subprocess没有完成执行,它将立即返回,等等。

使用非阻塞readlines的pexpect [ http://www.noah.org/wiki/Pexpect ]将解决此问题。 它源于pipe道被缓冲的事实,所以你的应用的输出被pipe道缓冲,因此在缓冲区填充或进程死亡之前,你不能到达那个输出。

我使用这个解决scheme来获取subprocess的实时输出。 该过程完成后,该循环将立即停止,而不再需要中断语句或可能的无限循环。

 sub_process = subprocess.Popen(my_command, close_fds=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) while sub_process.poll() is None: out = sub_process.stdout.read(1) sys.stdout.write(out) sys.stdout.flush() 

实时输出问题已解决:我在Python中遇到类似的问题,同时从c程序捕获实时输出。 我添加了“ fflush(stdout) ;” 在我的C代码。 它为我工作。 这里是snip的代码

<< C程序>>

 #include <stdio.h> void main() { int count = 1; while (1) { printf(" Count %d\n", count++); fflush(stdout); sleep(1); } } 

“Python程序”

 #!/usr/bin/python import os, sys import subprocess procExe = subprocess.Popen(".//count", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) while procExe.poll() is None: line = procExe.stdout.readline() print("Print:" + line) 

<< OUTPUT >>打印:计数1打印:计数2打印:计数3

希望它有帮助。

〜sairam

您可以在子stream程输出中的每个字节上使用迭代器。 这允许从subprocess内联更新(以'\ r'结尾的行覆盖之前的输出行):

 from subprocess import PIPE, Popen command = ["my_command", "-my_arg"] # Open pipe to subprocess subprocess = Popen(command, stdout=PIPE, stderr=PIPE) # read each byte of subprocess while subprocess.poll() is None: for c in iter(lambda: subprocess.stdout.read(1) if subprocess.poll() is None else {}, b''): c = c.decode('ascii') sys.stdout.write(c) sys.stdout.flush() if subprocess.returncode != 0: raise Exception("The subprocess did not terminate correctly.") 

完整解决scheme

 import contextlib import subprocess # Unix, Windows and old Macintosh end-of-line newlines = ['\n', '\r\n', '\r'] def unbuffered(proc, stream='stdout'): stream = getattr(proc, stream) with contextlib.closing(stream): while True: out = [] last = stream.read(1) # Don't loop forever if last == '' and proc.poll() is not None: break while last not in newlines: # Don't loop forever if last == '' and proc.poll() is not None: break out.append(last) last = stream.read(1) out = ''.join(out) yield out def example(): cmd = ['ls', '-l', '/'] proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # Make all end-of-lines '\n' universal_newlines=True, ) for line in unbuffered(proc): print line example() 

在这里find这个“即插即用”的function。 像魅力一样工作!

 import subprocess def myrun(cmd): """from http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html """ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = [] while True: line = p.stdout.readline() stdout.append(line) print line, if line == '' and p.poll() != None: break return ''.join(stdout) 

这就是我所做的:

 def read_command_output(command): process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return iter(process.stdout.readline, b"") for output_line in read_command_output(cmd): print(line) 

它连续打印新行,因为它们是由子过程输出的。