最新消息:

Python之系统交互(调用系统命令)subprocess

IT技术 ipcpu 4329浏览

在早期的Python版本中,我们主要是通过os.system()、os.popen().read()等函数来执行命令行指令的,另外还有一个很少使用的commands模块。但是从Python 2.4开始官方文档中建议使用的是subprocess模块。(本文的例子全部是以Python3.5为运行环境)

对于调用一个系统命令,我们最关心的有如下几点:

  1. 命令执行的状态码--表示命令执行是否成功
  2. 命令执行的输出结果--命令执行成功后或者错误后的输出

接下来,我们分别看下这几种方法。

一、os.system()和os.popen()的用法

我们先来看下os.system()的用法:

#!/usr/bin/python
import os

print('--os.system-code0--')
res = os.system('ls')
print(type(res))
print(res)
print('--os.system-codenot0-')
res = os.system('lsa')
print(type(res))
print(res)

输出结果如下

--os.system-code0--
aa.py anaconda-ks.cfg   debug.log original-ks.cfg
<class 'int'>
0
--os.system-codenot0-
sh: lsa: command not found
<class 'int'>
32512

结论:os.system(command)会返回命令执行的返回码,而将命令执行结果输出到屏幕。

os.popen()的代码如下

print('--os.popen--')
res = os.popen('ls')
print(type(res))
print(res.read())

输出结果如下

--os.popen--
<class 'os._wrap_close'>
aa.py
anaconda-ks.cfg
original-ks.cfg

结论:os.popen(command).read()可以获取命令执行结果,但是无法获取命令执行的返回码。
这两个命令,都功能不全呢?有没有既能拿到返回码,又能拿到输出结果的呢?有。

二、subprocess.run的用法

subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码。
在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能;
在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;

#!/usr/bin/python
import subprocess

print('--subprocess.run--')
res = subprocess.run(["ls", "-l"],stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True)
print(type(res))
print(res)
print('code: ',res.returncode,'stdout: ',res.stdout)

输出结果如下

--subprocess.run--
<class 'subprocess.CompletedProcess'>
CompletedProcess(args='ls', returncode=0, stdout=b'aa.py\nanaconda-ks.cfg\nbb.py\ndebug.log\noriginal-ks.cfg\n', stderr=b'')
code: 0 stdout: b'aa.py\nanaconda-ks.cfg\nbb.py\ndebug.log\noriginal-ks.cfg\n'

subprocess.run()返回了一个类,其中包含了程序运行返回码,和输出内容。
注意:如果加上了check=True参数,表示命令执行失败会抛出异常。

2.1 关于输出

我们可以看到,上面输出的结果是 b'xxxxx',这是一个bytes类型的数据。实际使用中需要将其转换为字符串格式。

#linux使用自动,或者uft8
print(res.stdout.decode())
#windows下字符编码是GB2312
print(res.stdout.decode('GB2312'))

2.2 输出到HTML时保留换行

默认情况下,将带有换行的数据字符串输出到HTML中,字符串会变成一行,没有换行,如果想保留原字符串的换行格式,需要使用HTML标签


,例如

<div class="box-body">
<pre> {{ content }} </pre>
</div>

2.3 关于shell参数

shell默认为False。

在Linux下,shell=False时, 如果args是字符串,那么只能是命令,不能包含任何参数,否则报错;如果args是一个列表list,则args的第一项是定义程序命令字符串,其它项是调用系统Shell时的附加参数。

shell=True时,如果args是字符串,Popen直接调用系统的Shell来执行,字符串格式和shell终端书写格式一样;如果args是一个列表list,则args的第一项是定义程序命令字符串,其它项是调用系统Shell时的附加参数。官方推荐shell=True时,使用字符串方式传递参数。

如果想使用shell中的管道,重定向,文件通配符,环境变量等功能,例如"ifconfig |grep eth0 > mm",那么只能使用shell=True,并且使用字符串来传递。

综上,shell=True功能最强大的,但因为强大也存在安全风险,需要谨慎的对待传递的参数。

2.4 关于超时

很多脚本运行时会卡住,导致调用脚本一直等待,这很显然不是我们想看到的,因此执行命令的超时Timeout设置很有必要。好在subprocess.run()提供了timeout参数,使用如下:

res = subprocess.run('/bin/bash /tmp/for.sh',stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True,timeout=30)

如果在30秒内无法执行完毕,会爆出异常。

subprocess.TimeoutExpired: Command '/bin/bash /tmp/for.sh' timed out after 30 seconds

我们也可以使用try、except捕捉这个异常

try:
    res = subprocess.run('/bin/bash /tmp/for.sh',stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True,timeout=30)
    print(type(res))
    print(res)
    print('code: ',res.returncode,'stdout: ',res.stdout)
except subprocess.TimeoutExpired as e:
    print(e)

三、subprocess.call和subprocess.getoutput用法

print('--subprocess.call--')
p = subprocess.call('ifconfig | grep eth0', stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True)
print(type(p))
print(p)
#只能得到返回码,得不到输出

print('--subprocess.getoutput--')
p = subprocess.getoutput('ifconfig | grep eth0')
print(type(p))
print(p)
#只能得到输出

显示结果:

--subprocess.call--
<class 'int'>
0
--subprocess.getoutput--
<class 'str'>
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

subprocess.call()只能返回状态码。subprocess.getoutput(cmd)只能输出命令结果。

四、subprocess.popen()的使用

实际上,上面的几个函数都是基于Popen()的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。

#!/usr/bin/python
import subprocess

print('--subprocess.popen--')
p = subprocess.Popen('ifconfig | grep eth0', stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True)
print(type(p))
print(p)
returncode=p.wait()
print(returncode)
#popen只有通过wait或者poll才能获得返回
print(p.stdout.read())
print(p.stderr.read())

执行结果如下:

--subprocess.popen--
<class 'subprocess.Popen'>
<subprocess.Popen object at 0x7fd3160812e8>
0
b'eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500\n'
b''

参考资料

https://www.cnblogs.com/yyds/p/7288916.html

转载请注明:IPCPU-网络之路 » Python之系统交互(调用系统命令)subprocess