【问题标题】:Python subprocess: Issues capturing error where part of command fail when piped togetherPython 子进程:问题捕获错误,其中部分命令在管道在一起时失败
【发布时间】:2018-07-12 00:39:10
【问题描述】:

在 bash 中执行一个简单的命令

cmd='ls -l | wc -l'

我知道我们可以使用 subprocess call/check_output/communicate 以多种方式运行此命令。如果初始命令由于某种原因不起作用或失败,则会出现问题。就像 [用 lsx 替换 ls]。

cmd='lsx -l | wc -l'

在这种情况下,我们如何捕获错误,或者我们只需要处理输出就可以了?这是我尝试过的。

import subprocess
>>> subprocess.call('lsx -l | wc -l', shell=True)
/bin/sh: lsx: command not found
       0
0
>>> subprocess.check_output('lsx -l | wc -l', shell=True)
/bin/sh: lsx: command not found
b'       0\n'

上面两条命令的错误码好像还是0。 我也试过https://docs.python.org/3.5/library/subprocess.html#replacing-shell-pipeline,但不知道如何获取第一个进程的错误代码。

【问题讨论】:

  • 为什么不将stderr分配给某个东西并检查它是否为空?
  • @Ranjit,...按照您链接的“替换 shell 管道”示例,很有可能获得退出代码——您能展示一下您的尝试吗?

标签: python linux subprocess pipe


【解决方案1】:

您可以将stderr 分配给PIPE。考虑这个例子:

>>> from subprocess import PIPE, Popen
>>> sub = Popen('lsx -l | wc -l', shell=True, stderr=PIPE, stdout=PIPE)
>>> output, error_output = sub.communicate()
>>> error_output
b'/bin/sh: 1: lsx: not found\n'
>>> output
b'0\n'
>>> sub = Popen('ls -l | wc -l', shell=True, stderr=PIPE, stdout=PIPE)
>>> output, error_output = sub.communicate()
>>> error_output
b''

【讨论】:

  • stderr 不是错误独有的——POSIX 指定它用于all 诊断日志记录。任何不打算通过管道传输到其他程序的输出都属于那里,因此通常会打印状态栏等。因此,“stderr 是非空的 -> 必须是失败的”在一般情况下并不是一个安全的假设。见Do progress reports / logging information belong on stderr or stdout?
【解决方案2】:

如果您指定bash 而不是sh,您可以设置pipefail 选项以在管道的任何部分失败时返回非零退出状态:

subprocess.check_output(['bash', '-c', 'set -o  pipefail; lsx -l | wc -l'])

也就是说,您当然可以完全避免使用shell=True

import subprocess
try:
    p1 = subprocess.Popen(['ls', '--invalid-argument'], stdout=subprocess.PIPE)
    p2 = subprocess.Popen(['wc', '-l'], stdin=p1.stdout, stdout=subprocess.PIPE)
    wc_stdout = p2.communicate()[0]
    if p1.wait() != 0 or p2.wait() != 0:
        raise RuntimeError("Something failed!")
except FileNotFoundError as ex:
    raise RuntimeError("Something failed, because we couldn't find an executable!") from ex

【讨论】:

  • 感谢您的意见。当我尝试有效参数时,程序仍然报告错误,例如将第 3 行更改为 p1 = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)
  • 嘿——你说得对; p1.returncode 是None,而不是0。谢谢,我会解决的。
  • @RanjitKumar 现在为两个进程显式调用wait(),这样就不会再发生了。
猜你喜欢
  • 2019-04-12
  • 2013-02-02
  • 2016-01-04
  • 1970-01-01
  • 2023-03-24
  • 2013-12-28
  • 2012-01-25
  • 2018-10-18
  • 2010-11-05
相关资源
最近更新 更多