【问题标题】:Python subprocess Popen.communicate() equivalent to Popen.stdout.read()?Python 子进程 Popen.communicate() 等价于 Popen.stdout.read()?
【发布时间】:2012-10-18 22:59:05
【问题描述】:

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

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

我也欢迎其他评论/建议(尽管我已经很清楚shell=True 的危险和跨平台限制)

请注意,我已经阅读了Python subprocess interaction, why does my process work with Popen.communicate, but not Popen.stdout.read()?,并且我不想/不需要之后与该程序进行交互。

另外请注意,我已经阅读了Alternatives to Python Popen.communicate() memory limitations?,但我并没有真正明白...

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

第一个代码:

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 wait popen communicate


    【解决方案1】:

    如果您查看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 确实使用了对stdoutstderr 的读取调用,并且还调用了wait()。这只是操作顺序的问题。在您的情况下,因为您将PIPE 用于stdout 和stderr,所以它进入_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 对象的方法。

    【讨论】:

      猜你喜欢
      • 2012-03-29
      • 2011-02-12
      • 2014-02-04
      • 2011-10-03
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 2012-08-16
      • 2011-02-04
      相关资源
      最近更新 更多