Python 编程过程中经常会用到系统命令,本文记录实现方法。
系统命令
- 作为胶水语言,Python可以很方便的执行系统命令,Python3中常用的执行操作系统命令有以下方式
- os.system()
- os.popen()
- subprocess 模块
os.system
- 执行命令但无法获取取命令输出时,可以使用
os.system
-
os.system()
是C语言system()
函数的封装,返回命令的退出状态码,命令执行结果输出到标准输出(stdout/窗口)。 - system函数可以将字符串转化成命令在服务器上运行;其原理是每一条system函数执行时,其会创建一个子进程在系统上执行命令行,子进程的执行结果无法影响主进程。
- 其返回值是指令运行后返回的状态码,0表示指令成功执行,1表示失败,256表示没有返回结果,该方法适用于系统命令不需要输出内容的场景。
- 用法:
os.system("command")
- 示例:
import os
command = "ifconfig"
exit_code = os.system(command)
# 执行 sh 脚本
os.system('sh /root/script/test,sh')
import os
a=os.system("ping 192.168.1.101") #使用a接收返回值
print(a)
# 理论上command是一个字符串,但实际看command还是得变为字节数组
# 当命令中存在中文时可能会报编码错误,此时可以自己给命令编一下码
# exit_code = os.system(command.encode("gbk"))
os.popen
- 这种调用方式是通过管道的方式来实现,这个函数的返回值是一个文件对象,可以读或者写(由mode决定,mode默认是’r’)。如果mode为’r’,调用该对象的
read()
或readlines()
方法可以读取输出内容。 - 用法:
os.popen(command[, mode[, bufsize]])
os.system(cmd)
或os.popen(cmd)
,前者返回值是脚本的退出状态码,后者的返回值是脚本执行过程中的输出内容。- 示例:
import os
command = "ifconfig"
command_output = os.popen(command).readlines()
print(command_output)
a=os.popen("ipconfig")
print(a.read())
>>>
以太网适配器 以太网:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe81::b0ed:2b1b:7385:97d1%8
IPv4 地址 . . . . . . . . . . . . : 192.168.1.100
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.1.1
subprocess
- subprocess模块是python从2.4版本开始引入的模块,主要用来取代 一些旧的模块方法,如os.system、os.spawn、os.popen、commands.*等。官方推荐使用该模块执行系统命令,subprocess模块通过子进程来执行外部指令,并通过input/output/error管道,获取子进程的执行的返回信息。 好处在于:运用对线程的控制和监控,将返回的结果赋于一变量,便于程序的处理。
subprocess.Popen()
使用Popen可以创建进程,并与进程进行复杂的交互。
- 用法:
child = subprocess.Popen(["cmd","arg1"...])
- 在一些复杂场景中,我们需要将一个进程的执行输出作为另一个进程的输入。在另一些场景中,我们需要先进入到某个输入环境,然后再执行一系列的指令等。这个时候我们就需要使用到suprocess的Popen()方法。该方法有以下参数
参数 | 作用 |
---|---|
args | 一般是一个字符串,是要执行的shell命令内容 |
bufsize | 设置缓冲,负数表示系统默认缓冲,0表示无缓冲,正数表示自定义缓冲行数 |
stdin | 程序的标准输入句柄,NONE表示不进行重定向,继承父进程,PIPE表示创建管道 |
stdout | 程序的标准输出句柄,参数意义同上 |
stderr | 程序的标准错误句柄,参数意义同上,特殊,可以设置成STDOUT,表示与标准输出一致 |
shell | 为True时,表示将通过shell来执行 |
cwd | 用来设置当前子进程的目录 |
env | 用于指定子进程的环境变量。如果env=None,则默认从父进程继承环境变量 |
universal_newlines | 不同系统的的换行符不同,当该参数设定为true时,则表示使用n作为换行符。 |
- Popen方法:
- 1、Popen.poll():用于检查子进程是否已经结束。设置并返回returncode属性。
- 2、Popen.wait():等待子进程结束。设置并返回returncode属性。
- 3、Popen.communicate(input=None):与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
- 4、Popen.send_signal(signal):向子进程发送信号。
- 5、Popen.terminate():停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
- 6、Popen.kill():杀死子进程。
- 7、Popen.stdin:如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。
- 8、Popen.stdout:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。
- 9、Popen.stderr:如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回None。
- 10、Popen.pid:获取子进程的进程ID。
- 11、Popen.returncode:获取进程的返回值。如果进程还没有结束,返回None。
- 12、subprocess.call(*popenargs, **kwargs):运行命令。该函数将一直等待到子进程运行结束,并返回进程的returncode。文章一开始的例子就演示了call函数。如果子进程不需要进行交互,就可以使用该函数来创建。
- 13、subprocess.check_call(*popenargs, **kwargs):与subprocess.call(*popenargs, **kwargs)功能一样,只是如果子进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包括进程的returncode信息
- 示例
from subprocess import PIPE, Popen
# 返回的是 Popen 实例对象
proc = Popen(
'ipconfig', # cmd特定的查询空间的命令
stdin=None, # 标准输入 键盘
stdout=PIPE, # -1 标准输出(演示器、终端) 保存到管道中以便进行操作
stderr=PIPE, # 标准错误,保存到管道
shell=True)
# print(proc.communicate()) # 标准输出的字符串 标准错误的字符串
outinfo, errinfo = proc.communicate()
print(outinfo.decode('gbk')) # 外部程序(windows系统)决定编码格式
print(errinfo.decode('gbk'))
>>>
以太网适配器 以太网:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe81::b0ed:2b1b:7385:97d1%8
IPv4 地址 . . . . . . . . . . . . : 192.168.1.100
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.1.1
Process finished with exit code 0
注意
- 在shell=True这个参数,不写的时候默认是False,shell默认为/bin/sh。如果 args是一个字符串,则该字符串指定要通过shell执行的命令。
- 当需要设置shell=True时(当False时,arges是列表,第一个参数是shell命令,后面的都是参数’,’ 隔开),须把args设为string,空格隔开,如下
>>> a = subprocess.Popen(['tail','-f', '/apps/apache-tomcat-8.5.29/logs/catalina.out'])
>>> a = subprocess.Popen('tail -f /apps/apache-tomcat-8.5.29/logs/catalina.out',shell=True)
subprocess.call()
- 执行指定的命令, 返回命令执行状态, 功能类似os.system(cmd),参数shell默认为False
- 用法:
subprocess.call("command")
- 示例:
# linux获取磁盘空间
import subprocess
subprocess.call(['df', '-h']) # 数组作为参数运行命令
输出:
Filesystem Size Used Avail Use% Mounted on
devtmpfs 909M 0 909M 0% /dev
tmpfs 920M 32K 920M 1% /dev/shm
tmpfs 920M 472K 919M 1% /run
tmpfs 920M 0 920M 0% /sys/fs/cgroup
/dev/vda1 50G 6.2G 41G 14% /
tmpfs 184M 0 184M 0% /run/user/0
subprocess.run()
- python3.5中新增的函数, 执行指定的命令, 等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。run默认不会返回输出,只返回命令和执行状态。
- 用法:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, universal_newlines=False)
import subprocess
completed = subprocess.run(['ls', '-1'])
print('returncode:', completed.returncode)
subprocess.getstatusoutput()
- 执行cmd命令,返回一个元组(命令执行状态,命令执行结果输出)。
- 返回状态码和结果,0表示成功
- 示例:
import subprocess
ret, val = subprocess.getstatusoutput("ping www.baidu.com")
print(ret)
print(val)
0
正在 Ping www.a.shifen.com [14.215.177.39] 具有 32 字节的数据:
来自 14.215.177.39 的回复: 字节=32 时间=29ms TTL=53
来自 14.215.177.39 的回复: 字节=32 时间=29ms TTL=53
来自 14.215.177.39 的回复: 字节=32 时间=29ms TTL=53
来自 14.215.177.39 的回复: 字节=32 时间=30ms TTL=53
14.215.177.39 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 29ms,最长 = 30ms,平均 = 29ms
代码语言:javascript复制import subprocess
command = "ifconfig"
# 命令如果带参数则不能直接使用字符串的形式,不然后报FileNotFoundError: [Errno 2] No such file or directory: 'ifconfig -a'等错误
# 此时有两种处理办法,一种是写成以空格为分割的列表形式。可借助shlex.split(command_line)实现自动分割
# command = ["ifconfig", "-a"]
# 别一种是将shell参数置为True
# completed_process_obj = subprocess.run(command, shell=True)
# 当配置了stdout参数,completed_process_obj的stdout属性就会保存命令的输出
completed_process_obj = subprocess.run(command, stdout=subprocess.PIPE)
print(completed_process_obj.stdout.decode())
# subprocess.Popen()基础参数和run()差不多,比run()对命令进程有更强的控制能力。其也能用来获取命令输出
# popen_obj = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
# print(popen_obj.stdout.readlines())
# 其他如subprocess.call()、subprocess.check_call()、subprocess.check_output()都是旧方法了,Python3.5 都建议用run()
subprocess.check_call()
- 与call方法类似,不同在于如果命令行执行成功,check_call返回返回码0,否则抛出subprocess.CalledProcessError异常。 subprocess.CalledProcessError异常包括returncode、cmd、output等属性,其中returncode是子进程的退出码,cmd是子进程的执行命令,output为None。
- 用法
subprocess.check_call(args, *, stdin = None, stdout = None, stderr = None, shell = False)
- 示例:
a = subprocess.check_call('df -h',shell=True)
>>>
文件系统 容量 已用 可用 已用% 挂载点
/dev/vda3 80G 3.6G 77G 5% /
devtmpfs 7.8G 0 7.8G 0% /dev
tmpfs 7.8G 0 7.8G 0% /dev/shm
tmpfs 7.8G 8.6M 7.8G 1% /run
tmpfs 7.8G 0 7.8G 0% /sys/fs/cgroup
/dev/vda6 404G 2.2G 402G 1% /data1
/dev/vda2 497M 128M 370M 26% /boot
tmpfs 1.2G 0 1.2G 0% /run/user/0
print(a)
>>>
0
参考资料
- https://www.cnblogs.com/lsdb/p/13068187.html
- https://docs.python.org/3/library/os.html#os.system
- https://zhuanlan.zhihu.com/p/95130482
- https://zhuanlan.zhihu.com/p/329957363