子进程检查输出错误

5
我正在尝试使用以下脚本检查check_output的用法,但遇到编译错误,我做错了什么?
import os
import subprocess
from subprocess import check_output

#result = subprocess.check_output(['your_program.exe', 'arg1', 'arg2'])
SCRIPT_ROOT=subprocess.check_output(["pwd","shell=True"])
print SCRIPT_ROOT

def main ():
    pass

if __name__ == '__main__':
    main()

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    from subprocess import check_output
ImportError: cannot import name check_output

1
这是一个无关的侧面说明,但是:你添加了 from subprocess import check_output 只是为了尝试调试这个问题吗?如果不是,当你已经 import 整个模块并在代码中显式地使用它限定(如 subprocess.check_output)时,你不想这样做。(另外,同时具有 if __name__ == '__main__': 习惯用法和模块级过程代码有点奇怪。如果代码不必被 import,就不要费心处理 __main__ 的东西。如果需要,请不要放置不应在 import 上运行的模块级代码。) - abarnert
2个回答

10

check_output在Python 2.7中被引入。如果您使用的是早期版本的Python,则不支持该函数。

另一种替代方法是使用Popen

output = subprocess.Popen(['pwd'], stdout=subprocess.PIPE).communicate()[0]

这一点的证明可以在此处找到。

新函数:子进程模块的check_output()运行一个带有指定参数集的命令,当命令正常运行时返回命令的输出字符串,否则引发CalledProcessError异常。

替代品示范。

import subprocess
cmd = subprocess.Popen(['pwd'], stdout=subprocess.PIPE)
output = cmd.communicate()[0]
print cmd.returncode
print output

输出

> python p.py
/Users/vlazarenko/tests
唯一真正的区别在于 Popen 在命令返回非零代码时不会抛出异常。

我在同一台使用2.6.5版本的Linux机器上使用check_call功能,你是说check_output是在check_call之后添加的吗? - user1927396
1
@favoretti:抱歉,我的意思是返回代码——告诉你“命令是否无误”的方式。这就是check_callcheck_output的全部意义,而你并没有这样做。 - abarnert
重写了示例,以便您可以提取返回代码,不过您需要自己检查 :) 我想一个 if 应该足够简单了。 - favoretti
这是因为communicate()间接设置了returncode,而不是直接设置,只有在进程终止后才会设置。所以基本上它在某些边缘情况下会失败。 - favoretti
@J.F.Sebastian:你忽略了一个重点,即returncode可能在你(直接或间接)调用pollwait之前不可用,因为调用pollwait是设置它的方法(至少在POSIX上),而不是进程退出。因此,check_output不关心poll() is None是否完全不同于从未调用poll()。如果我正确理解favoretti的话,这实际上确实会发生在使用communicate的代码中。 - abarnert
显示剩余8条评论

4

2.6版本中没有check_output函数。但是如果你查看2.7源代码,会发现这很简单。

事实上,处理这个问题的常见方式是:

import subprocess
try:
    check_output = subprocess.check_output
except AttributeError:
    def check_output(*popenargs, **kwargs):
        # lines 537-545 copied and pasted from 2.7 source

当然,这需要您的代码符合PSFL兼容性(它几乎肯定是,但如果有关系,我不会从SO上的随机人员那里获取法律建议)。
另一种选择是使用 subprocess32 ,这是将3.2.3 subprocess 模块回退到Python 2.4+的版本,它不仅提供了新的2.7功能和修复,还提供了许多更新的功能。 (个人而言,每当我编写2/3代码时,我最终都会编写 with subprocess.Popen… ,然后必须将其更改为 with contextlib.closing(subprocess.Popen… with subprocess32.Popen ...)

哈哈,即使我一开始没有去看那个源代码,但在我的回答中我也重新发明了几乎相同的轮子,尽管问题的作者一直说我的解决方案不能替代 ;) - favoretti
@favoretti:是的,但除非必要,我不会重新发明轮子。很容易忘记边缘情况或编写仅适用于您的Mac dev box而不是linux的代码,即使它们都是POSIX,或者…… 因此,除非我在一家拥有偏执律师的公司工作,否则我宁愿复制粘贴 - 或者,也许更好的是 pip install subprocess32 - abarnert
@favoretti:我曾经有一个真实的应用程序,在OS X、FreeBSD和linux 2.2-2.4上百分之百地工作,但在linux 2.6+上非常非常少地失败,这都是因为对于Popen.wait()实际上做了相同的事情的错误假设。也许这是一种被咬一次,就会害怕65535次的情况... - abarnert
@abamert:别误会,我并不是在反驳你的观点 - 我同意 :) 尽管我们的那位已经问了另一个问题,滥用了我建议的代码,甚至没有点赞 :( - favoretti
@J.F.Sebastian:我很友善,我正在尝试在他的下一个问题中帮助他 :) 我只是在发牢骚,抱歉 :) - favoretti
显示剩余2条评论

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