天天看點

python subprocess子產品

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)      
python subprocess子產品
python subprocess子產品
#!/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)      
python subprocess子產品
python subprocess子產品
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)      
python subprocess子產品
python subprocess子產品
[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')) # 輸出執行結果      
python subprocess子產品
python subprocess子產品
活動連接配接

  協定  本地位址          外部位址        狀态           解除安裝狀态

  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')) # 輸出執行結果      
python subprocess子產品
python subprocess子產品
'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")        
python subprocess子產品
python subprocess子產品
[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")      
python subprocess子產品
python subprocess子產品
[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/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。