subprocess

使用Python进行Linux系统管理与运维总是免不了在Python代码中执行shell命令、启动子进程,并捕获命令的输出和退出状态。
而subprocess模块就可以实现, 它最早是在python2.4版本中引入中。

call

call函数的返回值是命令的退出状态码,工程师可以通过退出状态码判命令是否执行成功, 成功返回0,否则返回非0

call函数执行的外部命令以一个字符串列表的形式进行传递

In [31]: status_code = subprocess.call(['ls', '-l'])
total 17700
drwxr-xr-x  9 nginx nginx     4096 Nov 10 00:10 nginx-1.12.2
-rw-r--r--  1 root  root    981687 Dec 22  2017 nginx-1.12.2.tar.gz
drwxr-xr-x 14 nginx nginx     4096 Nov 10 00:02 zabbix-3.4.4
-rw-r--r--  1 root  root  17132537 Dec 25  2017 zabbix-3.4.4.tar.gz

In [32]: status_code
Out[32]: 0
subprocess.call(['ls', '-l'])

如果设置了shell为True,则可以使用一个字符串命令,python将先运行一个shell,在用这个shell来解释整个字符串

In [34]: status_code = subprocess.call('ls -l', shell=True)
total 17700
drwxr-xr-x  9 nginx nginx     4096 Nov 10 00:10 nginx-1.12.2
-rw-r--r--  1 root  root    981687 Dec 22  2017 nginx-1.12.2.tar.gz
drwxr-xr-x 14 nginx nginx     4096 Nov 10 00:02 zabbix-3.4.4
-rw-r--r--  1 root  root  17132537 Dec 25  2017 zabbix-3.4.4.tar.gz

In [35]: status_code
Out[35]: 0
subprocess.call('ls -l', shell=True)

模拟执行失败

In [39]: status_code = subprocess.call('exit 1', shell=True)

In [40]: status_code
Out[40]: 1
subprocess.call('exit 1', shell=True)

check_call

 check_call函数的作用与call函数类似,区别在于异常情况下返回的形式不同,对于check_call函数,如果命令执行成功,返回0,如果执行失败,抛出suprocess.CalledProcessError异常

In [41]: status_code = subprocess.check_call('exit 1', shell=True)
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
<ipython-input-41-7ec365b00bf2> in <module>()
----> 1 status_code = subprocess.check_call('exit 1', shell=True)

/usr/lib64/python2.7/subprocess.pyc in check_call(*popenargs, **kwargs)
    540         if cmd is None:
    541             cmd = popenargs[0]
--> 542         raise CalledProcessError(retcode, cmd)
    543     return 0
    544 

CalledProcessError: Command 'exit 1' returned non-zero exit status 1
subprocess.check_call('exit 1', shell=True)

check_output

call和check_call函数直接将命令的输出结果输出到命令行终端。在实际工作中,一般会对获取的命令结果进行进一步的处理,或者将命令的输出打印到日志文件中。

In [42]: output = subprocess.check_output(['df', '-h'])

In [43]: print(output)
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        20G  2.3G   18G  12% /
devtmpfs        904M     0  904M   0% /dev
tmpfs           913M  4.0K  913M   1% /dev/shm
tmpfs           913M  9.7M  903M   2% /run
tmpfs           913M     0  913M   0% /sys/fs/cgroup
tmpfs           183M     0  183M   0% /run/user/0


In [44]: lines = output.split('\n')

In [46]: lines
Out[46]: 
['Filesystem      Size  Used Avail Use% Mounted on',
 '/dev/sda1        20G  2.3G   18G  12% /',
 'devtmpfs        904M     0  904M   0% /dev',
 'tmpfs           913M  4.0K  913M   1% /dev/shm',
 'tmpfs           913M  9.7M  903M   2% /run',
 'tmpfs           913M     0  913M   0% /sys/fs/cgroup',
 'tmpfs           183M     0  183M   0% /run/user/0',
 '']

 In [47]: for line in lines[1:-1]:
    ...:     if line:
    ...:         print(line.split()[-2])
    ...:         
12%
0%
1%
2%
0%
0%
View Code

check_ouput函数通过返回值来返回命令的执行结果,显然无法像call函数一样通过返回退出状态码表示异常情况。因此,check_output函数通过抛出一个subprocess.CalledProcessError异常来表示命令执行出错,如下:

try:
    output = subprocess.check_output(['cmd', 'arg1', 'arg2'])
except subprocess.CalledProcessError as e:
    output = e.output
    code = e.returncode

默认情况下,check_ouput命令只会捕获命令的标准输出。如果想捕获命令的错误输出,需要将输出重定向到标准输出。如下所示:

In [9]: try:
   ...:     subprocess.check_output(['rm', '-f', '/tmp/root.txt'], stderr=subprocess.STDOUT)
   ...: except subprocess.CalledProcessError as e:
   ...:     output = e.output
   ...:     code = e.returncode
   ...:            

In [10]: output
Out[10]: 'rm: cannot remove \xe2\x80\x98/tmp/root.txt\xe2\x80\x99: Operation not permitted\n'

Popen类

当以上便利函数无法满足业务的需求时,也可以直接使用Popen类。Popen类更具有灵活性,能够通过它处理更多复制的情况。

下面的函数对Popen执行shell命令进行封装,封装以后,只要将需要执行的shell命令传递给该函数即可。当命令执行成功时,将返回命令退出的状态码和标准输出,当命令执行失败时,将返回退出状态码和错误输出

import sys
import subprocess

def execute_cmd(cmd):
    p = subprocess.Popen(cmd,
                         shell=True,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    if p.returncode != 0:
        return p.returncode, stderr
    return p.returncode, stdout

print(execute_cmd(sys.argv[1]))

 

posted @ 2018-11-10 14:29  sellsa  阅读(260)  评论(0编辑  收藏  举报