为什么python的subprocess.call是这样实现的?

时间:2011-03-04 14:37:14

标签: python subprocess

子进程模块具有便捷功能call,在2.6和3.1中都是这样实现的:

def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()

此功能的文档带有红色警告,显示为:

  

警告:与Popen.wait()一样,使用stdout=PIPE和/或stderr=PIPE时会出现死锁,并且子进程会为管道生成足够的输出阻止等待OS管道缓冲区接受更多数据。

Popen.wait()文档说明在这种情况下使用Popen.communicate()。那么,为什么call没有像下面这样实现,所以可以删除愚蠢的警告,并从标准库中删除这样的愚蠢限制?

def call(*args, **kwargs):
    input = kwargs.pop("input", None)
    p = Popen(*args, **kwargs)
    p.communicate(input)
    return p.returncode

我确信这是有原因的。我错过了什么?

2 个答案:

答案 0 :(得分:8)

我花了一些时间浏览PEP-324,它引入了子流程模块,试图找出所涉及的设计决策,但我认为答案实际上非常简单:

没有理由stdout=PIPEstderr=PIPE传递给subprocess.call,因此它可以死锁的事实无关紧要。

stdout=PIPEstderr=PIPE传递给subprocess.Popen的唯一原因是您可以将Popen实例的stdoutstderr属性用作文件对象。由于subprocess.call永远不会让您看到Popen实例,因此PIPE选项变得无关紧要。

Popen.communicate可能存在开销(通过监视管道创建额外的线程以避免死锁),在这种情况下没有任何好处,因此没有理由使用它。

编辑:如果您想丢弃输出,我想最好明确地这样做:

# option 1
with open(os.devnull, 'w') as dev_null:
    subprocess.call(['command'], stdout=dev_null, stderr=dev_null)

# option 2
subprocess.call(['command >& /dev/null'], shell=True)

而不是指示子进程将所有输出捕获到您从未打算使用的PIPE文件。

答案 1 :(得分:2)

如果您要执行的只是运行命令并获取退出状态以确定它是成功还是失败,那么您不需要通过管道与它通信。这是subprocess.call()方法的便利。子进程模块中还有其他便利函数,它们有效地封装了使用Popen对象的许多常见用法。

如果需要将子进程stdout或stderr传递给某个地方而不是使用call(),请使用Popen对象并与doc(状态)通信()。