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

时间:2013-04-24 17:32:04

标签: 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))                                                                                  

我遇到的问题是抛出异常时,“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就可以了)并且可以访问退出代码。我可以根据退出代码手动检查通过/失败,但不会发生异常。

谢谢你, 艾哈迈德。

5 个答案:

答案 0 :(得分:78)

试试这个版本:

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,则打印返回代码和输出。

答案 1 :(得分:47)

已接受的解决方案涵盖了您可以混合stdoutstderr的情况,但是在子进程(无论出于何种原因)决定使用stderr的情况下添加对于未失败的输出(即输出非严重警告)到stdout,那么给定的解决方案可能是不可取的。

例如,如果你要对输出进行额外的处理,比如转换为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)

答案 2 :(得分:3)

两种建议的解决方案要么混合使用stdout / stderr,要么使用Popen,它不像check_output那么简单。但是,你可以完成同样的事情,并且在使用check_output的情况下保持stdout / stderr分开,如果你只是使用管道捕获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)。< / p>

答案 3 :(得分: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()

答案 4 :(得分:-2)

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

cmnd_output = ''