在早期的Python版本中,我们主要是通过os.system()、os.popen().read()等函数来执行命令行指令的,另外还有一个很少使用的commands模块。但是从Python 2.4开始官方文档中建议使用的是subprocess模块。(本文的例子全部是以Python3.5为运行环境)
对于调用一个系统命令,我们最关心的有如下几点:
- 命令执行的状态码--表示命令执行是否成功
- 命令执行的输出结果--命令执行成功后或者错误后的输出
接下来,我们分别看下这几种方法。
一、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