【问题标题】:Get exit code and stderr from subprocess call从子进程调用中获取退出代码和标准错误
【发布时间】:2013-04-18 09:23:12
【问题描述】:

我阅读了 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))                                                                                  

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

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

我认为这是因为异常导致“check_output”立即退出,无需任何进一步处理,也就是在 try 块中分配给“cmnd_output”。如果我错了,请纠正我。

有什么方法可以让我访问 stderr(如果它被发送到 stout 就可以了)并可以访问退出代码。我可以根据退出代码手动检查通过/失败,而不会抛出异常。

谢谢你, 艾哈迈德。

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    试试这个版本:

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

    这样您将仅在调用成功时打印输出。 如果是CalledProcessError,则打印返回码和输出。

    【讨论】:

    • 我收到这个错误:ret = subprocess.check_output(cmd , stderr=STDOUT,shell=True)NameError: global name 'STDOUT' is not defined
    • 完美!不要忘记stderr=STDOUT,当你尝试这种方法时
    • 使用 timeout 是因为 OP 的代码使用了它。显然,除了cmnd 之外的所有内容都是可选的。
    • 我认为这种方法会将输出混合到stdout和stderr。
    【解决方案2】:

    接受的解决方案涵盖您可以混合stdoutstderr 的情况,但在子进程(无论出于何种原因)决定使用stderr 以及stdout 的情况下非失败输出(即输出非严重警告),则给定的解决方案可能不理想。

    例如,如果您将对输出进行额外处理,例如转换为 JSON,并且您混合了 stderr,那么整个过程将失败,因为由于添加了 @,因此输出将不是纯 JSON 987654326@输出。

    我发现以下方法适用于这种情况:

    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 的必要性吗?我看到其他帖子在没有额外管道的情况下这样做,我不确定为什么。
    • 对于 python 2.7 至少这会崩溃,除非您将 shell=True 添加到 Popen
    • 谢谢,这很有用。在错误之后我还需要一个.decode("utf-8"),因为由于某种原因它们以字节为单位返回。
    • 这应该是公认的正确答案,谢谢
    【解决方案3】:

    两种建议的解决方案要么混合使用 stdout/stderr,要么使用 Popen,它不像 check_output 那样简单易用。但是,如果您只是使用管道捕获 stderr,则可以在使用 check_output 时完成相同的操作,并保持 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)。

    【讨论】:

    • 这个解决方案需要python >= 3.5
    • 我不是 Python 开发人员,因此请原谅可能的无知,但 Python 文档听起来他们建议this: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.
    • @Mike Popen 与communicate() 如果你有太多的输出也会爆炸,因为没有办法迭代处理两个流。从本质上讲,如果您想要做类似于tee 命令所做的事情,那么在没有大量文件描述符的情况下,这在Python 中是不可能的,并且通过系统调用完成所有工作。 subprocess API 存在设计缺陷。
    • 特别是早期版本中缺少的东西是CalledProcessError.stderr。以前的版本只有returncodecmdoutput
    【解决方案4】:

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

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

    【讨论】:

    • 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请edit您的答案添加解释并说明适用的限制和假设。
    【解决方案5】:

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

    cmnd_output = ''
    

    【讨论】:

    • 我需要 cmnd_output 由 stdout(以及 stderr)的内容初始化。在大多数情况下都会发生这种情况,除非程序的退出代码不为零。
    • 没错,所以如果出现异常,您的代码会打印异常,而变量中不会有任何内容。但是,当它正常运行时(没有例外),你会得到 cmnd_output 的值。
    猜你喜欢
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 2020-01-04
    • 1970-01-01
    • 2010-09-11
    • 2016-11-17
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多