调用进程错误的输出检查

57

жҲ‘жӯЈеңЁдҪҝз”ЁPythonзҡ„subprocessжЁЎеқ—дёӯзҡ„subprocess.check_outputжқҘжү§иЎҢpingе‘Ҫд»ӨгҖӮиҝҷжҳҜжҲ‘зҡ„д»Јз Ғпјҡ

output = subprocess.check_output(["ping","-c 2 -W 2","1.1.1.1")

代码引发了CalledProcessError异常,输出信息是该函数的一个参数。请问如何读取该输出内容?我想将其读入字符串并进行解析。举个例子,如果ping命令返回:

100%数据包丢失

我需要捕获这句话。如果有更好的方法,请建议。谢谢。


1
//你能在这个问题中包含一个链接 https://docs.python.org/2/library/subprocess.html#subprocess.CalledProcessError,以便查看文档吗? - Nathan Basanese
最重要的是关闭您打开的任何括号:对于每个 [,{ 或 (,必须有一个 ),} 或 ],然后将您的 shell 命令想象为一个字符串,并在其上执行 .split(' '),并将生成的列表作为输入传递给 check_output() - krysopath
6个回答

171
根据Python os模块文档,自Python 2.6起os.popen已被弃用。
我认为现代Python的解决方案是使用subprocess模块中的check_output()。
来自subprocess Python文档
“subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False) 以字节字符串形式运行带有参数的命令并返回其输出。 如果返回代码非零,则会引发CalledProcessError。 CalledProcessError对象将在returncode属性中具有返回代码,并在output属性中具有任何输出。”
如果您在Python 2.7(或更高版本)中运行以下代码:
import subprocess

try:
    print subprocess.check_output(["ping", "-n", "2", "-w", "2", "1.1.1.1"])
except subprocess.CalledProcessError, e:
    print "Ping stdout output:\n", e.output

您应该看到类似于以下输出:
Ping stdout output:

Pinging 1.1.1.1 with 32 bytes of data:
Request timed out.
Request timed out.

Ping statistics for 1.1.1.1:
Packets: Sent = 2, Received = 0, Lost = 2 (100% loss),

e.output字符串可以被解析以适应OP的需求。

如果您想要returncode或其他属性,它们在CalledProccessError中,可以通过pdb进行步进查看。

(Pdb)!dir(e)   

['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
 '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__',
 '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 
 '__unicode__', '__weakref__', 'args', 'cmd', 'message', 'output', 'returncode']

3
如果你不需要输出结果,建议使用check_call代替check_output。如果你知道命令在普通情况下返回非零退出状态,则可以直接使用Popen()并调用p.wait()output, err = p.communicate()(如果需要输出)以避免引发不必要的异常。 - jfs
@J.F.Sebastian 我尝试使用subprocess.call,但是这返回给我一个整数(1),而不是我的命令的输出 :/ - Loretta
@Loretta: 注意:请注意我的评论中的 “如果您不使用输出”。 如果 subprocess.call() 返回 1,则意味着您的命令已运行但失败:通常表示出现了错误,请在这种情况下使用 check_* 函数来引发异常。 如果在您的情况下它不是一个错误(例如对于 grep 它表示“未找到”); 那么可以捕获 CalledProcessError 或直接使用 Popen 及其方法。 - jfs
2
感谢您使用try except进行包装。 - jorfus
1
这段代码片段只适用于Python 2.7,而不是Python 3,因此您应该从其前面的声明中删除“(或更高版本)”。 - kbolino
显示剩余2条评论

26

如果您想获取标准输出和标准错误信息(包括从可能发生的CalledProcessError异常中提取它们),请使用以下代码:

import subprocess

def call_with_output(command):
    success = False
    try:
        output = subprocess.check_output(command, stderr=subprocess.STDOUT).decode()
        success = True 
    except subprocess.CalledProcessError as e:
        output = e.output.decode()
    except Exception as e:
        # check_call can raise other exceptions, such as FileNotFoundError
        output = str(e)
    return(success, output)

call_with_output(["ls", "-l"])

这是兼容Python 2和3的。

如果您的命令是字符串而不是数组,请在前面加上:

import shlex
call_with_output(shlex.split(command))

在我的端上,e.output.decode("utf-8") 返回 "" - alper
1
@alper 进程可能会在不生成任何输出的情况下失败。以下是一个微不足道的示例:call_with_output(["false"]) - Zags
如果你知道输出会是文本的话,使用.decode('utf-8')而不是依赖默认编码是个好主意。 - undefined

21

在参数列表中,每个条目必须单独放置。

output = subprocess.check_output(["ping", "-c","2", "-W","2", "1.1.1.1"])

应该可以解决你的问题。


我仍然收到错误。错误显示输出是CalledProcessError的一个参数之一。我尝试使用try except...但那也不起作用。 - ash
@ash,你能否发布一个简短的可重现示例,以证明except无法正常工作? - phihag
1
尝试:subprocess.check_output(["ping", "-c","2", "-W","2", "1.1.1.1") except CalledProcessError as e: print str(e.output)。这是我的 try catch 块。 - ash
不用担心这个问题...我通过使用Popen找到了一个解决方法...并读取了输出。谢谢你的帮助。 - ash
3
@ash,FYI应该是“除了subprocess.CalledProcessError”。 - Lelouch Lamperouge
7
这并没有回答这个问题。 - fiatjaf

2

我遇到了同样的问题,并发现文档中有一个针对这种情况的示例(我们将STDERR写入STDOUT并始终成功退出,返回代码为0),而不会引起或捕获异常。

output = subprocess.check_output("ping -c 2 -W 2 1.1.1.1; exit 0", stderr=subprocess.STDOUT, shell=True)

现在,您可以使用标准字符串函数find来检查输出字符串output


1

只有当主机响应 ping 时,此代码才会返回 true。适用于 Windows 和 Linux。

def ping(host):
    """
    Returns True if host (str) responds to a ping request.
    NB on windows ping returns true for success and host unreachable
    """
    param = '-n' if platform.system().lower()=='windows' else '-c'
    result = False
    try:
        out = subprocess.check_output(['ping', param, '1', host])
        #ping exit code 0
        if 'Reply from {}'.format(host) in str(out):
            result = True          
    except  subprocess.CalledProcessError:
        #ping exit code not 0
            result = False
    #print(str(out))
    return result

1

谢谢@krd,我正在使用您的错误捕获过程,但不得不更新打印和异常语句。我在Linux Mint 17.2上使用Python 2.7.6。

此外,输出字符串来源不清楚。 我的更新:

import subprocess

# Output returned in error handler
try:
    print("Ping stdout output on success:\n" + 
           subprocess.check_output(["ping", "-c", "2", "-w", "2", "1.1.1.1"]))
except subprocess.CalledProcessError as e:
    print("Ping stdout output on error:\n" + e.output)

# Output returned normally
try:
    print("Ping stdout output on success:\n" + 
           subprocess.check_output(["ping", "-c", "2", "-w", "2", "8.8.8.8"]))
except subprocess.CalledProcessError as e:
    print("Ping stdout output on error:\n" + e.output)

我看到了这样的输出:
Ping stdout output on error:
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.

--- 1.1.1.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1007ms


Ping stdout output on success:
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=37.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=59 time=38.8 ms

--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 37.840/38.321/38.802/0.481 ms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接