python子產品系列之 - subprocess
subprocess – 建立附加程序
subprocess子產品提供了一種一緻的方法來建立和處理附加程序,與标準庫中的其它子產品相比,提供了一個更進階的接口。用于替換如下子產品:
os.system() , os.spawnv() , os和popen2子產品中的popen()函數,以及 commands().
1、subprocess.call()
subprocess的call方法可以用于執行一個外部指令,但該方法不能傳回執行的結果,隻能傳回執行的狀态碼: 成功(0) 或 錯誤(非0)
import subprocess
ret = subprocess.call(["", "-h"])
print(ret)
ret = subprocess.call("df -h", shell=true)
print(ret)

#!/usr/bin/python
#! -*- coding:utf-8 -*-
import subprocess
ret = subprocess.call(["df", "-h"])
print(ret)
ret = subprocess.call("df -h", shell=True)
print(ret)
運作結果
如上執行個體所示,雖然我們能看到執行的結果,但實際擷取的值隻是狀态碼
2、subprocess.check_call() 方法
check_call與call指令相同,差別是如果出錯會報錯
我們說過call執行傳回一個狀态碼,我們可以通過check_call()函數來檢測指令的執行結果,如果出現錯誤,進行報錯【如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性,可用try…except…來檢查
import subprocess
ret = subprocess.check_call(["dd", "-h"]) # dd -h ,指令不存在
print(ret)

Traceback (most recent call last):
File "1.py", line 5, in <module>
ret = subprocess.check_call(["dd", "-h"])
File "/usr/local/python3/lib/python3.5/subprocess.py", line 271, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['dd', '-h']' returned non-zero exit status 1
使用try...excepe...來捕獲異常
import subprocess
try:
ret = subprocess.check_call("etstat -tn", shell = True)
except subprocess.CalledProcessError:
print("error")
else:
print(ret)

[root@ming python]# python 2.py
/bin/sh: etstat: command not found
error
3、subprocess.check_output()方法
父程序等待子程序執行指令,傳回子程序向标準輸出發送輸出運作結果,檢查退出資訊,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性和output屬性,output屬性為标準輸出的輸出結果,可用try…except…來檢查。
import subprocess
try:
ret = subprocess.check_output("netstat -tn", shell = True) #可以執行成功
except subprocess.CalledProcessError:
print("error")
else:
print(ret.decode('GBK')) # 輸出執行結果

活動連接配接
協定 本地位址 外部位址 狀态 解除安裝狀态
TCP 127.0.0.1:443 127.0.0.1:54149 ESTABLISHED InHost
TCP 127.0.0.1:443 127.0.0.1:57952 ESTABLISHED InHost
TCP 127.0.0.1:8307 127.0.0.1:54150 ESTABLISHED InHost
TCP 127.0.0.1:8307 127.0.0.1:57957 ESTABLISHED InHost
TCP 127.0.0.1:54149 127.0.0.1:443 ESTABLISHED InHost
TCP 127.0.0.1:54150 127.0.0.1:8307 ESTABLISHED InHost
import subprocess
try:
ret = subprocess.check_output("nestat -tn", shell = True) #指令錯誤
except subprocess.CalledProcessError:
print("error")
else:
print(ret.decode('GBK')) # 輸出執行結果

'nestat' 不是内部或外部指令,也不是可運作的程式
或批處理檔案。
error
4、subprocess.Popen()方法
實際上,subprocess子產品中隻定義了一個類: Popen。上面的幾個函數都是基于Popen()的封裝(wrapper)。從Python2.4開始使用Popen來建立程序,用于連接配接到子程序的标準輸入/輸出/錯誤中去,還可以得到子程序的傳回值。這些封裝的目的在于讓我們容易使用子程序。當我們想要更個性化我們的需求的時候,就要轉向Popen類,該類生成的對象用來代表子程序。
構造函數如下:
subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
與上面的封裝不同,Popen對象建立後,主程式不會自動等待子程序完成。我們必須調用對象的wait()方法,父程序才會等待 (也就是阻塞block)。
a、父程序不等待子程序
import subprocess
child = subprocess.Popen('ping www.baidu.com', shell= True)
print("hello")

[root@ming python]# python 3.py
hello
PING www.a.shifen.com (220.181.112.244) 56(84) bytes of data.
64 bytes from 220.181.112.244: icmp_seq=1 ttl=57 time=1.93 ms
64 bytes from 220.181.112.244: icmp_seq=2 ttl=57 time=3.28 ms
64 bytes from 220.181.112.244: icmp_seq=3 ttl=57 time=3.62 ms
64 bytes from 220.181.112.244: icmp_seq=4 ttl=57 time=1.45 ms
64 bytes from 220.181.112.244: icmp_seq=5 ttl=57 time=1.46 ms
可以看出,Python并沒有等到child子程序執行的Popen操作完成就執行了print操作。
b、添加子程序等待
import subprocess
child = subprocess.Popen('ping -c 4 www.baidu.com', shell= True)
child.wait() #子程序等待
print("hello")

[root@ming python]# python 3.py
PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data.
64 bytes from 220.181.111.188: icmp_seq=1 ttl=59 time=2.01 ms
64 bytes from 220.181.111.188: icmp_seq=2 ttl=59 time=2.28 ms
64 bytes from 220.181.111.188: icmp_seq=3 ttl=59 time=8.70 ms
64 bytes from 220.181.111.188: icmp_seq=4 ttl=59 time=1.82 ms
--- www.a.shifen.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3041ms
rtt min/avg/max/mdev = 1.824/3.707/8.701/2.888 ms
hello
可以看出,print語句是等待child子程序執行完後才執行的
此外,你還可以在父程序中對子程序進行其它操作,比如我們上面例子中的child對象:
child.poll() # 檢查子程序狀态
child.kill() # 終止子程序
child.send_signal() # 向子程序發送信号
child.terminate() # 終止子程序
ps: 子程序的PID存儲在child.pid
c、子程序文本流控制
子程序的标準輸入、标準輸出和标準錯誤如下屬性分别表示:
child.stdin | child.stdout | child.stderr
我們還可以在Popen()建立子程序的時候改變标準輸入、标準輸出和标準錯誤,并可以利用subprocess.PIPE将多個子程序的輸入和輸出連接配接在一起,構成管道(pipe):
例=1
#!/usr/bin/env python
import subprocess
child = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE)#将标準輸出定向輸出到subprocess.PIPE
print(child.stdout.read().decode("UTF-8")) #使用 child.communicate() 輸出的是一個元組
運作結果
[root@ming python]# python 5.py
total 20
-rw-r--r--. 1 root root 176 Dec 5 00:50 1.py
-rw-r--r--. 1 root root 210 Dec 5 01:03 2.py
-rw-r--r--. 1 root root 108 Dec 5 01:47 3.py
-rw-r--r--. 1 root root 178 Dec 6 20:39 4.py
-rw-r--r--. 1 root root 194 Dec 6 21:52 5.py
例=2
#!/usr/bin/env python
import subprocess
child1 = subprocess.Popen(['cat','/etc/passwd'],stdout=subprocess.PIPE)
child2 = subprocess.Popen(['grep','root'],stdin=child1.stdout,stdout=subprocess.PIPE)
a = child2.communicate() #是一個元組
for i in a:
if i:
print(i.decode("UTF-8"))
運作結果
[root@ming python]# python 6.py
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
subprocess.PIPE實際上為文本流提供一個緩存區。child1的stdout将文本輸出到緩存區,随後child2的stdin從該PIPE中将文本讀取走。child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
注意:communicate()是Popen對象的一個方法,該方法會阻塞父程序,直到子程序完成
作者:楊永明
出處:https://www.cnblogs.com/ming5218/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。