2013-04-24 72 views
32

我详细阅读了子流程提供的函数 - call,check_call,check_output,并了解它们各自的工作方式以及功能上的不同。我目前使用check_output,这样我就可以有机会获得标准输出,并使用“try块”来捕获异常,如下所示:从子流程调用中获取退出代码和stderr

# "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”没有初始化,没有访问到标准错误,我也得到了以下错误消息:

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

我想那是因为异常导致‘check_output’立即保释没有任何进一步的处理,又名分配到“cmnd_output”,在try块中。如果我错了,请纠正我。

有没有什么办法可以访问stderr(没关系,如果它被发送到stout)并且可以访问退出代码。我可以手动检查通过/失败的基础上退出代码与例外throuwn。

谢谢, 艾哈迈德。

回答

44

试试这个版本:

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的情况下,您打印返回码和输出。

+0

太棒了。这个解决方案非常完美。谢谢。 – 2013-04-24 18:37:35

+2

我收到此错误:'RET = subprocess.check_output(CMD,标准错误= STDOUT,壳=真)'' NameError:全局名称 '标准输出' 不defined' – ARH 2014-07-16 18:06:04

+5

@ARH,HTTPS://docs.python。 org/3/library/subprocess.html#subprocess.STDOUT – warvariuc 2014-07-17 05:22:49

-2

为什么不在try语句之前初始化varible cmd_output?这样它会按照你期望的方式工作。 行之后会工作,只是将它加入上述try语句:

cmnd_output = '' 
+0

我需要通过标准输出(也是stderr)来初始化cmnd_output。在大多数情况下会发生这种情况,除非程序的退出代码不为零。 – 2013-04-24 17:42:24

+0

确切的说,如果发生异常,你的代码会打印出这个异常,你不会在变量中有任何东西。但是,当它正常运行(没有例外)时,您将获得cmnd_output的值。 – n3rV3 2013-04-24 17:46:25

32

接受的解决方案涵盖了你都OK混合stdoutstderr的情况,但在案件中,子进程(无论何种原因)决定使用stderr除了stdout一个非失败输出(即输出一个非严重警告),那么给定的解决方案可能不合意。例如,如果您将对输出执行其他处理(如转换为JSON),并混合使用stderr,则整个过程将失败,因为输出将不是纯JSON,因为输出的stderr输出已添加了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) 
+0

非常有用。这也是我的用例。 stdout被我的目标用作更多的日志输出而不是错误输出。顺便说一句,你可以评论提供'stderr = subprocess.PIPE'的必要性吗?我已经看到其他职位没有额外的管道,我不知道为什么。 – KobeJohn 2016-03-18 03:51:42

+0

对于python 2.7,至少会崩溃,除非您向Popen添加shell = True – 2017-10-11 18:14:45

0

提出的解决方案的两个或者标准错误混合标准输出/,或使用Popen这是不是很简单check_output使用。但是,您可以完成同样的事情,并保持标准输出/标准错误分开,同时采用check_output,如果你只是捕获标准错误用管:

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属性(不用管道捕获,它只是None)。