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


71

我通读了子流程提供的功能-调用,检查_调用,检查_输出,并了解它们各自的工作原理以及功能上的不同。我当前正在使用check_output,因此我可以访问stdout,并使用“ try块”来捕获异常,如下所示:

# "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))                                                                                  

我遇到的问题是引发异常时,“ cmnd_output”未初始化并且无法访问stderr,并且出现以下错误消息:

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

我认为那是因为异常导致try块中的“ check_output”立即保释,而没有进行任何进一步的处理,即“ cmnd_output”。如果我错了,请纠正我。

有什么办法可以访问stderr(如果将其发送到stout,可以)并可以访问退出代码。我可以根据退出代码手动检查通过/失败,而不会引发异常。

谢谢,艾哈迈德。

Answers:


105

试试这个版本:

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))

这样,仅当调用成功时,才打印输出。如果是a CalledProcessError,则打印返回码和输出。


2
我收到此错误:ret = subprocess.check_output(cmd , stderr=STDOUT,shell=True) NameError: global name 'STDOUT' is not defined
— ARH 2014年

1
完善!stderr=STDOUT当您尝试这种方法时,请不要忘记
— VicX'2

timeout之所以使用,是因为OP中的代码已使用它。显然,除以外的所有内容cmnd都是可选的。
— warvariuc '18 -10-20

我认为这种方法会将输出混合到stdout和stderr。
— AuBee

65

接受的解决方案涵盖在其中,你都OK混合的情况下stdout和stderr,但在案件中,子进程(无论何种原因)决定使用stderr除stdout用于非失败输出(即输出非严重警告),然后给定的解决方案可能不是理想的。

例如,如果您将对输出进行其他处理(例如转换为JSON),然后混合使用stderr,则整个过程将失败,因为由于添加了输出,输出将不是纯JSON stderr。

在这种情况下,我发现以下方法可以工作:

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

pipes = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#If you are using python 2.x, you need to include shell=True in the above line
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)

很有用。这也是我的用例。我的目标将stdout用作日志输出,而不是错误输出。顺便说一句,您能否评论提供的必要性stderr=subprocess.PIPE?我看过其他帖子在没有附加管道的情况下这样做,我不确定为什么。
— 科比·约翰

1
对于python 2.7,至少会崩溃,除非您将shell = True添加到
— Popen

1
谢谢,这非常有用。.decode("utf-8")由于某种原因,它们以字节返回时,我也需要在err之后。
— cardamom

这应该是被接受的正确答案,谢谢您
— grepit

10

两种建议的解决方案要么混合使用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())))

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


5
此解决方案需要python> = 3.5
— uranix

4
我不是一个Python开发,所以原谅了潜在的无知,但Python文档听起来像他们建议没有做这个:Note: Do not use stderr=PIPE with this function as that can deadlock based on the child process error volume. Use Popen with the communicate() method when you need a stderr pipe.
— 麦克

1
如果您的输出太多,则带有communication()的@Mike Popen也将爆炸,因为无法迭代处理两个流。本质上,如果您想执行与tee命令所执行的操作类似的操作,则在很大程度上,在Python中,如果没有很多文件描述符,并且通过系统调用完成所有工作,则是不可能的。该subprocessAPI是由设计缺陷。
— wvxvw

具体来说,早期版本缺少的是CalledProcessError.stderr。以前的版本只returncode,cmd和output。
— 迈克尔·多斯特

0

我有类似的要求,以下对我有用:

    try:
        with open ("vtcstderr.out", "w") as file:
            rawOutput = subprocess.check_output(
                command,
                stderr=file,
                shell=True
            )
    except subprocess.CalledProcessError as error:
        # this is the stdout
        rawOutput = error.output

    with open ("vtcstderr.out", "r") as file:
        # this is the stderr
        errorLines = file.readlines()


2
尽管这段代码可以解决问题,但包括解释如何以及为什么解决该问题的说明,确实可以帮助提高您的帖子质量,并可能导致更多的投票。请记住,您将来会为读者回答问题,而不仅仅是现在问的人。请编辑您的答案以添加说明,并指出适用的限制和假设。
— БогданОпир20年

-2

为什么不在try语句之前初始化变量cmnd_output?这样,它将按您期望的方式工作。下一行将起作用,只需在try语句上方添加它:

cmnd_output = ''

我需要cmnd_output由stdout(以及stderr)的内容初始化。在大多数情况下,除非程序的退出代码为非零,否则会发生这种情况。
— 艾哈迈德

的确如此,以防万一发生异常,您的代码将打印该异常,并且变量中将没有任何内容。但是,当它正常运行时(无异常),您将获得cmnd_output的值。
— n3rV3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.