Python子进程Popen.communicate()相当于Popen.stdout.read()?

问题描述 投票:0回答:1

非常具体的问题(我希望):以下三个代码有什么区别?

(我希望只是第一个不等待子进程完成,而第二个和第三个则等待。但我需要确定这是唯一的区别...)

我也欢迎其他评论/建议(尽管我已经很清楚

shell=True
危险和跨平台限制)

请注意,我已经阅读了 Python 子进程交互,为什么我的进程可以与 Popen.communicate 一起工作,但不能与 Popen.stdout.read() 一起工作? 并且我不想/不需要在之后与程序进行交互。

另请注意,我已经阅读了 Python Popen.communicate() 内存限制的替代方案?但我并没有真正理解...

最后,请注意,我知道当一个缓冲区使用一种方法填充一个输出时,在某个地方存在死锁的风险,但我在互联网上寻找明确的解释时迷失了......

第一个代码:

from subprocess import Popen, PIPE

def exe_f(command='ls -l', shell=True):
    """Function to execute a command and return stuff"""

    process = Popen(command, shell=shell, stdout=PIPE, stderr=PIPE)

    stdout = process.stdout.read()
    stderr = process.stderr.read()

    return process, stderr, stdout

第二个代码:

from subprocess import Popen, PIPE
from subprocess import communicate

def exe_f(command='ls -l', shell=True):
    """Function to execute a command and return stuff"""

    process = Popen(command, shell=shell, stdout=PIPE, stderr=PIPE)

    (stdout, stderr) = process.communicate()

    return process, stderr, stdout

第三个代码:

from subprocess import Popen, PIPE
from subprocess import wait

def exe_f(command='ls -l', shell=True):
    """Function to execute a command and return stuff"""

    process = Popen(command, shell=shell, stdout=PIPE, stderr=PIPE)

    code   = process.wait()
    stdout = process.stdout.read()
    stderr = process.stderr.read()

    return process, stderr, stdout
python subprocess popen
1个回答
39
投票

如果您查看

subprocess.communicate()
的源代码,它显示了差异的完美示例:

def communicate(self, input=None):
    ...
    # Optimization: If we are only using one pipe, or no pipe at
    # all, using select() or threads is unnecessary.
    if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
        stdout = None
        stderr = None
        if self.stdin:
            if input:
                self.stdin.write(input)
            self.stdin.close()
        elif self.stdout:
            stdout = self.stdout.read()
            self.stdout.close()
        elif self.stderr:
            stderr = self.stderr.read()
            self.stderr.close()
        self.wait()
        return (stdout, stderr)

    return self._communicate(input)

您可以看到

communicate
确实利用了对
stdout
stderr
的读取调用,并且还调用了
wait()
。这只是操作顺序的问题。在您的情况下,因为您对 stdout 和 stderr 使用
PIPE
,所以它会进入
_communicate()
:

def _communicate(self, input):
    stdout = None # Return
    stderr = None # Return

    if self.stdout:
        stdout = []
        stdout_thread = threading.Thread(target=self._readerthread,
                                         args=(self.stdout, stdout))
        stdout_thread.setDaemon(True)
        stdout_thread.start()
    if self.stderr:
        stderr = []
        stderr_thread = threading.Thread(target=self._readerthread,
                                         args=(self.stderr, stderr))
        stderr_thread.setDaemon(True)
        stderr_thread.start()

    if self.stdin:
        if input is not None:
            self.stdin.write(input)
        self.stdin.close()

    if self.stdout:
        stdout_thread.join()
    if self.stderr:
        stderr_thread.join()

    # All data exchanged.  Translate lists into strings.
    if stdout is not None:
        stdout = stdout[0]
    if stderr is not None:
        stderr = stderr[0]

    # Translate newlines, if requested.  We cannot let the file
    # object do the translation: It is based on stdio, which is
    # impossible to combine with select (unless forcing no
    # buffering).
    if self.universal_newlines and hasattr(file, 'newlines'):
        if stdout:
            stdout = self._translate_newlines(stdout)
        if stderr:
            stderr = self._translate_newlines(stderr)

    self.wait()
    return (stdout, stderr)

这使用线程同时从多个流中读取。然后它在最后调用

wait()

总结一下:

  1. 此示例一次从一个流读取,并且不等待它完成该过程。
  2. 此示例通过内部线程同时从两个流中读取数据,并等待其完成该过程。
  3. 此示例等待进程完成,然后一次读取一个流。正如您所提到的,如果写入流的内容过多,则可能会出现死锁。

此外,您在第二个和第三个示例中不需要这两个 import 语句:

from subprocess import communicate
from subprocess import wait

它们都是

Popen
对象的方法。

© www.soinside.com 2019 - 2024. All rights reserved.