subprocess

使用Python進行Linux系統管理與運維老是免不了在Python代碼中執行shell命令、啓動子進程,並捕獲命令的輸出和退出狀態。
而subprocess模塊就能夠實現, 它最先是在python2.4版本中引入中。python

call

call函數的返回值是命令的退出狀態碼,工程師能夠經過退出狀態碼判命令是否執行成功, 成功返回0,不然返回非0nginx

call函數執行的外部命令以一個字符串列表的形式進行傳遞shell

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)

模擬執行失敗python2.7

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異常ide

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異常來表示命令執行出錯,以下:spa

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類更具備靈活性,可以經過它處理更多複製的狀況。3d

下面的函數對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]))

相關文章
相關標籤/搜索