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

我阅读了子流程调用、 check _ call、 check _ output 提供的函数,并了解了每个函数的工作原理以及它们之间的功能差异。我当前使用 check _ output,因此可以访问标准输出,并使用“ 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 就可以了)并且访问退出代码。我可以手动检查通过/失败的基础上退出代码没有抛出异常。

谢谢, 艾哈迈德。

95896 次浏览

why not initialize the varible cmnd_output before the try statement? That way it will work the way you expect it to. Following line would work, just add it above try statement :

cmnd_output = ''

Try this version:

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

This way you will print the output only if the call was successful. In case of a CalledProcessError you print the return code and the output.

The accepted solution covers the case in which you are ok mixing stdout and stderr, but in cases in which the child process (for whatever reason) decides to use stderr IN ADDITION to stdout for a non failed output (i.e. to output a non-critical warning), then the given solution may not desirable.

For example, if you will be doing additional processing on the output, like converting to JSON, and you mix in the stderr, then the overall process will fail since the output will not be pure JSON because of the added stderr output.

I've found the following to work in that case:

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)

Both of the proposed solutions either mix the stdout/stderr, or use Popen which isn't quite as simple to use as check_output. However, you can accomplish the same thing, and keep stdout/stderr separate, while using check_output if you simply capture stderr by using a pipe:

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

In this example, since we captured stderr, it's available in the exception's stderr attribute (without capturing with the pipe, it would just be None).

I had a similar requirement, and the following worked for me:

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