从子进程调用中获取退出代码和stderr

Ahm*_*d A 60 python python-3.x

我阅读了subprocess提供的函数 - call,check_call,check_output,并了解每个函数的工作方式和功能之间的差异.我目前正在使用check_output,所以我可以访问stdout,并使用"try block"来捕获异常,如下所示:

# "cmnd" is a string that contains the command along with it's arguments. 
try:
    cmnd_output = check_output(cmnd, stderr=STDOUT, shell=True, timeout=3, universal_newlines=True);                         
except CalledProcessError:                                                                                                   
    print("Status : FAIL")                                                                                                   
print("Output: \n{}\n".format(cmnd_output))                                                                                  
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是抛出异常时,"cmnd_output"未初始化并且无法访问stderr,我收到以下错误消息:

print("Output: \n{}\n".format(cmnd_output))
UnboundLocalError: local variable 'cmnd_output' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

我认为那是因为异常导致"check_output"立即保释而没有任何进一步的处理,也就是在try块中分配给"cmnd_output".如果我错了,请纠正我.

有没有什么方法可以访问stderr(如果它被发送到stout就没关系)并且可以访问退出代码.我可以根据退出代码手动检查通过/失败,但不会发生异常.

艾哈迈德,谢谢你.

war*_*iuc 88

试试这个版本:

import subprocess
try:
    output = subprocess.check_output(
        cmnd, stderr=subprocess.STDOUT, shell=True, timeout=3,
        universal_newlines=True)
except subprocess.CalledProcessError as exc:
    print("Status : FAIL", exc.returncode, exc.output)
else:
    print("Output: \n{}\n".format(output))
Run Code Online (Sandbox Code Playgroud)

这样,只有在呼叫成功时才会打印输出.如果CalledProcessError您打印返回代码和输出.

  • @ARH,https://docs.python.org/3/library/subprocess.html#subprocess.STDOUT (5认同)
  • 我收到此错误:`ret = subprocess.check_output(cmd,stderr = STDOUT,shell = True)``NameError:全局名称'STDOUT'未定义 (2认同)

oar*_*alo 53

接受的解决方案涵盖了你都OK混合的情况下stdoutstderr,但在案件中,子进程(无论何种原因)决定使用stderrstdout用于非失败输出(即输出非严重警告),然后给定的解决方案可能不合适.

例如,如果您将对输出执行其他处理,例如转换为JSON,并且混合使用stderr,则整个过程将失败,因为输出将不是纯JSON,因为添加了stderr输出.

在这种情况下我发现以下内容:

cmd_args = ... what you want to execute ...

pipes = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
std_out, std_err = pipes.communicate()

if pipes.returncode != 0:
    # an error happened!
    err_msg = "%s. Code: %s" % (std_err.strip(), pipes.returncode)
    raise Exception(err_msg)

elif len(std_err):
    # return code is 0 (no error), but we may want to
    # do something with the info on std_err
    # i.e. logger.warning(std_err)

# do whatever you want with std_out
# i.e. json.loads(std_out)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这非常有用。在错误之后我还需要一个 `.decode("utf-8")` ,因为由于某种原因它们以字节为单位返回。 (3认同)
  • 很有用。这也是我的用例。我的目标更多地使用 stdout 作为日志输出而不是错误输出。顺便问一下,您能评论一下提供 `stderr=subprocess.PIPE` 的必要性吗?我见过其他帖子在没有额外管道的情况下这样做,我不知道为什么。 (2认同)

Kyl*_*yle 5

两种建议的解决方案要么混合使用stdout / stderr,要么使用Popen不像那样简单的方法check_output。但是,check_output如果仅通过使用管道捕获stderr,则可以完成同一件事,并保持stdout / stderr分开:

import sys
import subprocess

try:
    subprocess.check_output(cmnd, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
    print('exit code: {}'.format(e.returncode))
    print('stdout: {}'.format(e.output.decode(sys.getfilesystemencoding())))
    print('stderr: {}'.format(e.stderr.decode(sys.getfilesystemencoding())))
Run Code Online (Sandbox Code Playgroud)

在此示例中,由于我们捕获了stderr,因此可在异常的stderr属性中使用它(如果不使用管道进行捕获,则为None)。

  • 我不是Python开发人员,因此请原谅潜在的无知之处,但Python文档听起来像他们建议_not_做[this](https://docs.python.org/2/library/subprocess.html#subprocess.check_output) :`注意:请勿将此函数与stderr = PIPE一起使用,因为这会基于子进程错误量而死锁。当您需要stderr管道时,可以将Popen与communication()方法一起使用。 (3认同)
  • 此解决方案需要python> = 3.5 (2认同)
  • 如果输出太多,@Mike Popen 和communicate() 也会爆炸,因为没有办法迭代处理两个流。本质上,如果你想要做一些类似于“tee”命令所做的事情,那么总的来说,在Python中,如果没有大量的文件描述符的舞蹈,并通过系统调用完成所有的工作,那是不可能的。“subprocess” API 在设计上存在缺陷。 (2认同)