使用Python進行Linux系統管理與運維老是免不了在Python代碼中執行shell命令、啓動子進程,並捕獲命令的輸出和退出狀態。
而subprocess模塊就能夠實現, 它最先是在python2.4版本中引入中。python
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
若是設置了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
模擬執行失敗python2.7
In [39]: status_code = subprocess.call('exit 1', shell=True) In [40]: status_code Out[40]: 1
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
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%
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類更具備靈活性,可以經過它處理更多複製的狀況。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]))