subprocess.check_output() 在 Python 2.6.5 中似乎不存在。

79

我一直在阅读有关Python子进程模块的文档(请参见这里),其中提到了一个subprocess.check_output()命令,这似乎正是我所需要的。

然而,当我尝试使用它时,会出现一个错误,提示它不存在,在运行dir(subprocess)时也没有列出该命令。

我正在运行Python 2.6.5,并且使用的代码如下:

import subprocess
subprocess.check_output(["ls", "-l", "/dev/null"])

有人知道为什么会发生这种情况吗?

3个回答

123

它在2.7版本中引入。请参阅文档

如果您需要输出,请使用subprocess.Popen

>>> import subprocess
>>> output = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE).communicate()[0]

14
与check_output不同,当进程返回非零返回码时,它不会引发“CalledProcessError”。 - Sridhar Ratnakumar
1
@SridharRatnakumar:当然,因为它们之间有很大的区别,即:阻塞和非阻塞。它们用于不同的用例! - László Papp
我把它放在一个 lambda 中,像这样:check_output = lambda args: Popen(args, stdout=PIPE).communicate()[0]。只是因为我在交互式解释器中,写多行函数定义有点麻烦。我之前在会话中使用了 from subprocess import Popen, PIPE - ArtOfWarfare
如何进行 ping?我还可以使用 Popen 吗? - TheCrazyProfessor

58

如果在你要运行的代码中大量使用了subprocess但是不需要长期维护该代码(或者你需要一个快速修复而不考虑未来可能出现的维护问题),那么你可以在导入subprocess的地方进行“鸭子打补丁”(也称为monkey patch)

只需从2.7中提取代码并插入即可...

import subprocess

if "check_output" not in dir( subprocess ): # duck punch it in!
    def f(*popenargs, **kwargs):
        if 'stdout' in kwargs:
            raise ValueError('stdout argument not allowed, it will be overridden.')
        process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
        output, unused_err = process.communicate()
        retcode = process.poll()
        if retcode:
            cmd = kwargs.get("args")
            if cmd is None:
                cmd = popenargs[0]
            raise subprocess.CalledProcessError(retcode, cmd)
        return output
    subprocess.check_output = f

可能需要轻微地调整。

但要记住,你需要维护这样的不干净的后端口。如果在最新版本的Python中发现并纠正了错误,则你需要a)注意到并b)更新你的版本,如果你想保持安全。此外,自己覆盖和定义内部函数是下一个人的噩梦,特别是当下一个人是你几年后,并且你已经忘记了上次做出的恶心的修改!总之:这很少是一个好主意。


2
我同意这种方法。我可能会包括源代码的位置。你可以在http://hg.python.org/cpython/file/d37f963394aa/Lib/subprocess.py#l544找到它。 - Ehtesh Choudhury
1
注意:在Python 2.6中,CalledProcessError不接受输出。(使用此技巧后我立即遇到了麻烦! :( ) - Andy Hayden
cpython现在已经在GitHub上了 - Python 2.7的check_output目前在这里:https://github.com/python/cpython/blob/2.7/Lib/subprocess.py#L194 - jamesc

6

感谢Monkey Patch的建议(因为我的尝试失败了——但是我们正在使用CalledProcessError输出,所以需要Monkey Patch它)

在这里找到一个适用于2.6的补丁: http://pydoc.net/Python/pep8radius/0.9.0/pep8radius.shell/

"""Note: We also monkey-patch subprocess for python 2.6 to
give feature parity with later versions.
"""
try:
    from subprocess import STDOUT, check_output, CalledProcessError
except ImportError:  # pragma: no cover
    # python 2.6 doesn't include check_output
    # monkey patch it in!
    import subprocess
    STDOUT = subprocess.STDOUT

    def check_output(*popenargs, **kwargs):
        if 'stdout' in kwargs:  # pragma: no cover
            raise ValueError('stdout argument not allowed, '
                             'it will be overridden.')
        process = subprocess.Popen(stdout=subprocess.PIPE,
                                   *popenargs, **kwargs)
        output, _ = process.communicate()
        retcode = process.poll()
        if retcode:
            cmd = kwargs.get("args")
            if cmd is None:
                cmd = popenargs[0]
            raise subprocess.CalledProcessError(retcode, cmd,
                                                output=output)
        return output
    subprocess.check_output = check_output

    # overwrite CalledProcessError due to `output`
    # keyword not being available (in 2.6)
    class CalledProcessError(Exception):

        def __init__(self, returncode, cmd, output=None):
            self.returncode = returncode
            self.cmd = cmd
            self.output = output

        def __str__(self):
            return "Command '%s' returned non-zero exit status %d" % (
                self.cmd, self.returncode)
    subprocess.CalledProcessError = CalledProcessError

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接