Python Popen - 等待与通信与 CalledProcessError

Python Popen - wait vs communicate vs CalledProcessError

继续 我看到要获取我在 python 中通过 Popen 生成的进程的错误代码,我必须调用 wait() 或 communicate() (可用于访问 Popen stdout 和 stderr 属性):

app7z = '/path/to/7z.exe'
command = [app7z, 'a', dstFile.temp, "-y", "-r", os.path.join(src.Dir, '*')]
process = Popen(command, stdout=PIPE, startupinfo=startupinfo)
out = process.stdout
regCompressMatch = re.compile('Compressing\s+(.+)').match
regErrMatch = re.compile('Error: (.*)').match
errorLine = []
for line in out:
    if len(errorLine) or regErrMatch(line):
        errorLine.append(line)
    if regCompressMatch(line):
        # update a progress bar
result = process.wait() # HERE
if result: # in the hopes that 7z returns 0 for correct execution
    dstFile.temp.remove()
    raise StateError(_("%s: Compression failed:\n%s") % (dstFile.s, 
                       "\n".join(errorLine)))

但是 the docs 警告 wait() 可能会死锁(当 stdout=PIPE 时,这里就是这种情况),而 communicate() 可能会溢出。所以:

  1. 这里用什么合适?请注意,我确实使用了输出
  2. 我应该如何使用 communicate ?会不会是:

    process = Popen(command, stdout=PIPE, startupinfo=startupinfo)
    out = process.communicate()[0]
    # same as before...
    result = process.returncode
    if result: # ...
    

    不确定阻塞和内存错误

  3. 任何 better/more pythonic 处理问题的方法?我不认为 subprocess.CalledProcessError or the subprocess.check_call/check_output 适用于我的情况 - 或者它们适用吗?

免责声明:我没有编写代码,我是当前的维护者,因此问题 3。

相关:

我在 windows 如果这有影响 - python 2.7.8

应该有一种——最好只有一种——显而易见的方法

  • 关于死锁:将 stdout=PIPEwait() 一起使用是安全的 iff​​ 您从管道中读取。 .communicate() 为您读书和打电话 wait()
  • 关于内存:如果输出可以是无限的那么你不应该使用 .communicate() 在内存中累积 all 输出。

what is the proper thing to use here ?

要启动子进程,逐行读取它的输出并等待它退出:

#!/usr/bin/env python
from subprocess import Popen, PIPE

process = Popen(command, stdout=PIPE, bufsize=1)
with process.stdout:
    for line in iter(process.stdout.readline, b''): 
        handle(line)
returncode = process.wait() 

此代码不会因有限 OS 管道缓冲区而死锁。此外,该代码支持具有无限输出的命令(如果单独的一行适合内存)。

iter() 用于在刷新子进程的标准输出缓冲区后立即读取一行,以解决 the read-ahead bug in Python 2. You could use a simple for line in process.stdout if you don't need to read lines as soon as they are written without waiting for the buffer to fill or the child process to end. See Python: read streaming input from subprocess.communicate().

如果您知道命令输出在所有情况下都可以放入内存中,那么您可以一次获得所有输出:

#!/usr/bin/env python
from subprocess import check_output

all_output = check_output(command)

如果命令 returns 具有非零退出状态,它会引发 CalledProcessError。在内部,check_output() uses Popen() and .communicate()

There should be one-- and preferably only one --obvious way to do it

subprocess.Popen() 是主要的 API,在许多情况下都有效。对于常见用例,有方便 functions/methods,例如 Popen.communicate()check_output()check_call()

有多种方法和功能,因为有多种不同的用例。