在进程运行时不断打印subprocess输出

要从我的Python脚本启动程序,我使用下面的方法:

def execute(command): process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = process.communicate()[0] exitCode = process.returncode if (exitCode == 0): return output else: raise ProcessException(command, exitCode, output) 

所以当我启动一个像Process.execute("mvn clean install")这样的进程时,我的程序一直等到进程结束,然后才得到程序的完整输出。 如果我正在运行需要一段时间才能完成的进程,这很烦人。

我可以让我的程序逐行写入进程输出,在循环结束之前轮询输出进程吗?

** [编辑]抱歉,我发现这个问题之前没有很好地search。 线程实际上是关键。 在这里find一个示例来演示如何执行它:** Python Subprocess.Popen从一个线程

一旦命令输出,你可以使用iter来处理行: lines = iter(fd.readline, "")

一个完整的例子(感谢@JF塞巴斯蒂安帮助了很多细节):

 from __future__ import print_function # Only Python 2.x import subprocess def execute(cmd): popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) for stdout_line in iter(popen.stdout.readline, ""): yield stdout_line popen.stdout.close() return_code = popen.wait() if return_code: raise subprocess.CalledProcessError(return_code, cmd) # Example for path in execute(["locate", "a"]): print(path, end="") 

好吧,我设法解决它没有线程(任何build议,为什么使用线程会更好,赞赏)通过使用从这个问题的代码片段在运行时拦截subprocess的标准输出

 def execute(command): process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Poll process for new output until finished while True: nextline = process.stdout.readline() if nextline == '' and process.poll() is not None: break sys.stdout.write(nextline) sys.stdout.flush() output = process.communicate()[0] exitCode = process.returncode if (exitCode == 0): return output else: raise ProcessException(command, exitCode, output) 

一旦在Python 3中刷新标准输出缓冲区,就要逐行输出子输出:

 from subprocess import Popen, PIPE, CalledProcessError with Popen(cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as p: for line in p.stdout: print(line, end='') # process line here if p.returncode != 0: raise CalledProcessError(p.returncode, p.args) 

注意:你不需要p.poll() – 到达eof时循环结束。 你不需要它iter(p.stdout.readline, '')iter(p.stdout.readline, '')读错误在Python 3中得到了修复。

另请参阅Python:从subprocess.communicate()读取stream式input 。

@tokland

尝试了你的代码,并纠正它为3.4和Windows dir.cmd是一个简单的DIR命令,保存为cmd文件

 import subprocess c = "dir.cmd" def execute(command): popen = subprocess.Popen(command, stdout=subprocess.PIPE,bufsize=1) lines_iterator = iter(popen.stdout.readline, b"") while popen.poll() is None: for line in lines_iterator: nline = line.rstrip() print(nline.decode("latin"), end = "\r\n",flush =True) # yield line execute(c) 

对于任何人尝试这个问题的答案来从Python脚本得到标准输出,注意Python缓冲它的标准输出,因此可能需要一段时间才能看到标准输出。

这可以通过在目标脚本中写入每个stdout后添加以下内容来解决:

 sys.stdout.flush() 

如果有人想使用线程同时读取stdoutstderr ,这就是我想到的:

 import threading import subprocess import Queue class AsyncLineReader(threading.Thread): def __init__(self, fd, outputQueue): threading.Thread.__init__(self) assert isinstance(outputQueue, Queue.Queue) assert callable(fd.readline) self.fd = fd self.outputQueue = outputQueue def run(self): map(self.outputQueue.put, iter(self.fd.readline, '')) def eof(self): return not self.is_alive() and self.outputQueue.empty() @classmethod def getForFd(cls, fd, start=True): queue = Queue.Queue() reader = cls(fd, queue) if start: reader.start() return reader, queue process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdoutReader, stdoutQueue) = AsyncLineReader.getForFd(process.stdout) (stderrReader, stderrQueue) = AsyncLineReader.getForFd(process.stderr) # Keep checking queues until there is no more output. while not stdoutReader.eof() or not stderrReader.eof(): # Process all available lines from the stdout Queue. while not stdoutQueue.empty(): line = stdoutQueue.get() print 'Received stdout: ' + repr(line) # Do stuff with stdout line. # Process all available lines from the stderr Queue. while not stderrQueue.empty(): line = stderrQueue.get() print 'Received stderr: ' + repr(line) # Do stuff with stderr line. # Sleep for a short time to avoid excessive CPU use while waiting for data. sleep(0.05) print "Waiting for async readers to finish..." stdoutReader.join() stderrReader.join() # Close subprocess' file descriptors. process.stdout.close() process.stderr.close() print "Waiting for process to exit..." returnCode = process.wait() if returnCode != 0: raise subprocess.CalledProcessError(returnCode, command) 

我只是想分享一下,因为我在这个问题上做了类似的尝试,但没有一个答案能解决我的问题。 希望它可以帮助别人!

请注意,在我的用例中,一个外部进程杀死了我们Popen()的进程。