Python subprocess模块

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命令。

上一篇:Java16:枚举类型


下一篇:记IBM JDK1.7 兼容 SUN JDK AES 解密