从subprocess.communicate()读取流输入

Hei*_*ing 75 python subprocess

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

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

subprocess.communicate() 似乎立刻提供所有输出.

jfs*_*jfs 143

一旦子进程刷新其stdout缓冲区,就要逐行获取子进程的输出:

#!/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
Run Code Online (Sandbox Code Playgroud)

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

如果子进程'stdout在非交互模式下使用块缓冲而不是行缓冲(这会导致输出延迟,直到子缓冲区已满或由子进程显式刷新),那么您可以尝试强制使用无缓冲输出 pexpect,pty模块unbuffer,stdbuf,script公用事业,见问:为什么不直接使用管道(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='')
Run Code Online (Sandbox Code Playgroud)

注意:与Python 2不同,它按原样输出子进程'bytestrings; Python 3使用文本模式(cmd的输出使用locale.getpreferredencoding(False)编码解码).

  • `b''是Python 2.7和Python 3中的`bytes`字面. (3认同)
  • @JinghaoShi:`bufsize = 1`可能会有所不同,如果你也*写*(使用`p.stdin`)到子进程,例如,它可以帮助避免死锁,同时做一个交互式(`pexpect`-like)交换 - 假设子进程本身没有缓冲问题.如果您只是阅读,那么我说的差异仅在于性能:如果不是这样,那么您能提供一个显示它的最小完整代码示例吗? (2认同)
  • @saulspatz 如果 `stdout=PIPE` 没有捕获输出(你仍然可以在屏幕上看到它),那么你的程序可能会打印到 stderr 或直接打印到终端。要合并 stdout&stderr,请传递 `stderr=subprocess.STDOUT`(请参阅我之前的评论)。要捕获直接打印到 tty 的输出,您可以 [使用 pexpect, pty 解决方案。](http://stackoverflow.com/a/25945031/4279)。这是一个[更复杂的代码示例](http://stackoverflow.com/a/29085418/4279)。 (2认同)

unu*_*tbu 41

请注意,我认为JF Sebastian的方法(下面)更好.


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

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

如果ls结束太快,则while循环可能在您读取所有数据之前结束.

您可以通过这种方式捕获stdout中的剩余部分:

output = proc.communicate()[0]
print output,
Run Code Online (Sandbox Code Playgroud)

  • 缓冲区阻塞比它有时听起来更简单:父块等待子进程退出+子块等待父进程读取并释放通信管道中的一些空间= full = deadlock.就这么简单.管道越小越容易发生. (5认同)

D C*_*zee 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
Run Code Online (Sandbox Code Playgroud)

readline()read()函数只应在EOF返回一个空字符串,进程终止后-否则,如果没有什么阅读(它会阻止readline()包括换行,等空行,则返回"\n").这避免了communicate()在循环之后需要进行尴尬的最终调用.

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

  • `data = proc.stdout.read()` 阻塞直到读取*所有*数据。您可能会将它与可以提前返回的 `os.read(fd, maxsize)` 混淆(只要有任何数据可用)。 (4认同)

归档时间:

查看次数:

72804 次

最近记录:

6 年,3 月 前