subprocess模块
subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。
在subprocess模块中启动子进程,最简单的方式就是使用这里的便利函数。当这些便利函数不能满足函数时,在使用底层的Popen类。便利函数包括call、check_all与check_output,run(此方法在python2中没有,可在python3中使用),下面将对这些进行记录.
subprocess模块的便利函数
1)call
call 函数的定义如下:
subprocess.call([args,*,stdin=True,stdout=None,stderr=None,shell=False])
call函数将运行由args参数指定的命令直到命令结束。call函数的返回值是命令的退出状态码,可以通过退出状态码判断命令是否执行成功,例:
>>> import subprocess >>> subprocess.call(['ls','-l']) total 24 -rw-r--r-- 1 root root 783 Feb 20 22:11 find_files.py -rwxr-xr-x 1 root root 411 Feb 20 22:20 oswalk.py.bak -rw-r--r-- 1 root root 12 Feb 21 19:18 read.txt drwxr-xr-x 2 root root 4096 Feb 20 21:06 t1 -rwxr-xr-x 1 root root 166 Feb 21 19:30 tarfile.py -rw-r--r-- 1 root root 271 Feb 21 19:30 tarfile.pyc 0 >>> subprocess.call('exit 1',shell=True) 1 #call函数执行的外部命令以一个字符串列表的形式进行传递,如果设置了shell为True,则可以使用一个字符串命令,而不是一个字符串列表来运行子进程。如果设置了shell为True, Python将先运行一个shell,在用这个shell来解释整个字符串
>>> subprocess.call('ls -l',shell=True) #设置shell为True,可以直接将命令写在一个字符串里 total 24 -rw-r--r-- 1 root root 783 Feb 20 22:11 find_files.py -rwxr-xr-x 1 root root 411 Feb 20 22:20 oswalk.py.bak -rw-r--r-- 1 root root 12 Feb 21 19:18 read.txt drwxr-xr-x 2 root root 4096 Feb 20 21:06 t1 -rwxr-xr-x 1 root root 166 Feb 21 19:30 tarfile.py -rw-r--r-- 1 root root 271 Feb 21 19:30 tarfile.pyc 0
获取执行结果的返回码
>>> import subprocess >>> retcode = subprocess.call(["ls", "-l"]) //此命令会在当前终端输出查询结果,并把命令执行返回值赋值给retcode,此变量可自定义,而subprocess_checkout命令含有一个默认的返回值为returncode,可通过根据此值判断命令是否执行成功 >>> print retcode //打印命令执行的返回结果即为命令的返回值 0
2)check_call
check_call函数的作用与call函数类似,区别在于异常情况下返回的形式不同。对于call函数,我们通过捕获call命令的返回值判断命令是否执行成功,如果成功返回0,否则返回非0。对于check_call函数,如果执行成功,返回0,如果执行失败,跑出subprocess.CallProcessError异常,例:
>>> ret = subprocess.check_call(['ls','-l']) total 24 -rw-r--r-- 1 root root 783 Feb 20 22:11 find_files.py -rwxr-xr-x 1 root root 411 Feb 20 22:20 oswalk.py.bak -rw-r--r-- 1 root root 12 Feb 21 19:18 read.txt drwxr-xr-x 2 root root 4096 Feb 20 21:06 t1 -rwxr-xr-x 1 root root 166 Feb 21 19:30 tarfile.py -rw-r--r-- 1 root root 271 Feb 21 19:30 tarfile.pyc >>> ret #命令执行的返回结果即为执行的结果,为0则执行成功,执行失败则直接报错
0 >>> subprocess.check_call('exit 1',shell=True) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python2.7/subprocess.py", line 542, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
3)run python3中使用
>>> ret =subprocess.run('ls -l /root',shell=True) total 137668 -rw-r--r-- 1 root root 2476 Apr 12 21:44 17_install_redis.sh -rw-r--r-- 1 root root 18471766 Jun 24 16:11 access-settlecenter.huoli.local-http.log -rw------- 1 root root 120489984 Mar 27 15:16 consul.tar drwxr-xr-x 2 root root 4096 Sep 12 18:18 devops drwxrwxr-x 6 root root 4096 May 16 2019 redis-5.0.5 -rw-r--r-- 1 root root 1975750 Jun 8 2020 redis-5.0.5.tar.gz -rw-r--r-- 1 root root 105 Sep 5 16:21 sys_stdin.py -rw-r--r-- 1 root root 159 Apr 15 15:31 sys_test.py drwxr-xr-x 2 root root 4096 Aug 2 16:37 tablesql -rw-r--r-- 1 root root 187 Sep 5 16:03 test.py >>> ret CompletedProcess(args='ls -l /root', returncode=0) >>> type(ret) #命令执行的返回结果为一个对象 <class 'subprocess.CompletedProcess'> >>> ret.returncode #对象.returncode为命令执行结果 0
以上不论是subprocess.call,subprocess.check_call还是subprocess.run方法,都会将命令的执行结果输出在终端,这种返回结果并不是我们想要的到的,一般会对命令的结果进行进一步的处理,或者将命令的输出打印到日志文件中,此时我们就是使用到subprocess.check_output()方法。
4) check_output
[root@config test]# cat check_output.py #!/usr/bin/python from __future__ import print_function import subprocess output=subprocess.check_output(['df','-h']) print(output) lines=output.split('\n') for line in lines: if line: print(line.split()[-2]) [root@config test]# python check_output.py Filesystem Size Used Avail Use% Mounted on devtmpfs 486M 0 486M 0% /dev tmpfs 496M 0 496M 0% /dev/shm tmpfs 496M 57M 439M 12% /run tmpfs 496M 0 496M 0% /sys/fs/cgroup /dev/vda1 40G 8.2G 30G 22% / tmpfs 100M 0 100M 0% /run/user/0 overlay 40G 8.2G 30G 22% /var/lib/docker/overlay2/abd499dd2c76192670d51ca193ae471e7c31b839b4a5da581a0ee8fb2fb03f36/merged shm 64M 0 64M 0% /var/lib/docker/containers/f336cd4eeaa20c0b3ae42ce96368abfb8d6263ad443e11b06511be9674ad176b/mounts/shm Mounted 0% 0% 12% 0% 22% 0% 22% 0%
check_output函数通过返回值返回命令的执行结果,显然无法像call函数一样通过返回退出状态码表示异常情况。因此,check_output函数通过抛出一个subprocess.CalledPorcessError异常来表示命令的异常来表示命令执行出错,例:
try: output = subprocess.check_output(['cmd','arg1','arg2']) //此次output为命令执行的结果存在此变量中,print(output)即为命令的输出结果,output.returncode则为命令执行返回值 except subprocess.CalledProcessError as e: output = e.output code = e.returncode
subprocess模块的Popen类
subprocess模块提供的便利函数都是对Popen类的封装,当便利函数无法满足业务的需求时,也可以直接使用Popen类。Popen类更具有灵活性,通过它能处理更多复杂的情况。Popen类的构造函数如下:
class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0,restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
Popen的基本使用方式与上一小节中介绍的便利函数类似。在Linux系统中,当shell设置为True时,shell默认使用/bin/sh。args是需要执行的命令,可以是一个命令字符串,也可以是一个字符串列表。
Popen对象创建后,子进程便会运行。Popen类提供了若干方法来控制子进程的运行,包括:
poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。 wait(timeout): 等待子进程终止。 communicate(input,timeout): 和子进程交互,发送和读取数据。 send_signal(singnal): 发送信号到子进程 。 terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。 kill(): 杀死子进程。发送 SIGKILL 信号到子进程。
其中communicate函数可以与子进程进行交互,包括输入数据,获取子命令的标准输出和错误输出。下面的函数对Popen执行shell命令进行封装,封装以后,只需要将要执行的shell命令传递给函数即可。当命令执行成功时,将返回命令的退出状态码和标准输出,当命令执行失败时,将返回退出状态码和错误输出。
import subprocess import os def execute_cmd(cmd): p = subprocess.Popen(cmd, shell=True, cwd=path //切换工作目录 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout,stderr = p.communicate() if p.returnmode != 0: return p.returnmode stderr return p.returnmode stdout
通过Python标准库的subprocess模块,可以运行外部程序,极大的拓展了Python的功能。在日常运维工作中,我们可以使用subprocess在python中执行复杂的linux命令。