- subprocess模塊介紹
subprocess是python創建子進程的工具,其實和c中的fork出一個子進程,然后在子進程中運行exec執行另外一個進程很類似。
subprocess包中有很多方法創建子進程,這些函數創建子進程的行為不太一樣,我們可以更具需求選擇不同的方式來創建子進程。
使用subprocess包中的函數創建子進程的時候,要注意:
1) 在創建子進程之后,父進程是否暫停,並等待子進程運行。
2) 函數返回什么
3) 當returncode不為0時,父進程如何處理。
- subprecess.call()
subprocess.call()
父進程等待子進程完成
返回退出信息(returncode,相當於exit code,見Linux進程基礎)
subprocess.check_call()
父進程等待子進程完成
返回0
檢查退出信息,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬 性,可用try...except...來檢查(見Python錯誤處理)。
subprocess.check_output()
父進程等待子進程完成
返回子進程向標准輸出的輸出結果
檢查退出信息,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性和output屬性,output屬性為標准輸出的輸出結果,可用try...except...來檢查。
1 #include <iostream> 2 #include <unistd.h> 3 4 using namespace std; 5 6 int main(int argc, const char *argv[]) 7 { 8 cout << "Python is powerful" << endl; 9 for (int i = 0; i < argc; i++) 10 { 11 cout << argv[i] << endl; 12 } 13 sleep(10); 14 return 0; 15 } 16 ~
1 #!/usr/bin/env python 2 3 import subprocess 4 5 returnCode = subprocess.call('ls -l',shell=True)
//我們使用了shell=True這個參數。這個時候,我們使用一整個字符串,而不是一個表來運行子進程。Python將先運行一個shell,再用這個shell來解釋這整個字符串。
6 print "returnCode:",returnCode 7 8 returnCode = subprocess.call(['ls','-l'])
//我們將程序名(ls)和所帶的參數(-l)一起放在一個表中傳遞給subprocess.call()
9 print "returnCode:",returnCode 10 11 returnCode = subprocess.call(['./app','-a','-b','-c','-d'])
//app也將參數和app本身以一個列表為傳遞過去
12 print "returnCode:",returnCode
執行結果:
yca@ubuntu:~/Desktop/go$ ./assert.py total 1256 -rwxr-xr-x 1 yca yca 7785 2013-05-07 20:02 app -rw-r--r-- 1 yca yca 221 2013-05-07 20:01 app.cpp -rwxr-xr-x 1 yca yca 217 2013-05-07 20:40 assert.py -rwxr-xr-x 1 yca yca 1256270 2013-04-28 02:30 hello -rw-r--r-- 1 yca yca 396 2013-05-01 19:59 hello.go -rw-r--r-- 1 yca yca 918 2013-05-07 01:08 HelloWorld.go -rw-r--r-- 1 yca yca 556 2013-05-07 02:43 map.go returnCode: 0 Python is powerful ./app -a -b -c -d returnCode: 0
- subprocess.Popen()
上面三個函數都是對subprocess.Popen的封裝,這些封裝的目的是為了讓我們容易使用子進程。當我們想要更個性化我們的需求的時候,就要轉向Popen類,該類生成的對象用來代表子進程。
與上面的封裝不同,Popen對象創建后,主程序不會自動等待子進程完成。我們必須調用對象的wait()方法,父進程才會等待 (也就是阻塞block):
1 #!/usr/bin/env python 2 3 import subprocess 4 5 returnCode = subprocess.Popen(['./app','-a','-b','-c','-d']) 6 print "parent process"
yca@ubuntu:~/Desktop/go$ ./assert.py parent process yca@ubuntu:~/Desktop/go$ Python is powerful ./app -a -b -c -d
從運行結果中看到,父進程在開啟子進程之后並沒有等待child的完成,而是直接運行print。
1 #!/usr/bin/env python 2 3 import subprocess 4 5 child = subprocess.Popen(['./app','-a','-b','-c','-d']) 6 returnCode = child.wait() 7 print "returnCode:",returnCode
8 print "parent process"
yca@ubuntu:~/Desktop/go$ ./assert.py Python is powerful ./app -a -b -c -d
returnCode:0 parent process
很明顯父進程在等待子進程執行完畢,才開始執行
此外,你還可以在父進程中對子進程進行其它操作,比如我們上面例子中的child對象:
child.poll() # 檢查子進程狀態
child.kill() # 終止子進程
child.send_signal() # 向子進程發送信號
child.terminate() # 終止子進程
1.Popen.poll():用於檢查子進程是否已經結束。設置並返回returncode屬性。 2.Popen.wait():等待子進程結束。設置並返回returncode屬性。 3.Popen.communicate(input=None):與子進程進行交互。向stdin發送數據,或從stdout和stderr中讀取數據。可選參數input指定發送到子進程的參數。Communicate()返回一個元組:(stdoutdata, stderrdata)。注意:如果希望通過進程的stdin向其發送數據,在創建Popen對象的時候,參數stdin必須被設置為PIPE。同樣,如果希望從stdout和stderr獲取數據,必須將stdout和stderr設置為PIPE。 4.Popen.send_signal(signal):向子進程發送信號。 5.Popen.terminate():停止(stop)子進程。在windows平台下,該方法將調用Windows API TerminateProcess()來結束子進程。 6.Popen.kill():殺死子進程。 7.Popen.stdin:如果在創建Popen對象是,參數stdin被設置為PIPE,Popen.stdin將返回一個文件對象用於策子進程發送指令。否則返回None。 8.Popen.stdout:如果在創建Popen對象是,參數stdout被設置為PIPE,Popen.stdout將返回一個文件對象用於策子進程發送指令。否則返回None。 9.Popen.stderr:如果在創建Popen對象是,參數stdout被設置為PIPE,Popen.stdout將返回一個文件對象用於策子進程發送指令。否則返回None。 10.Popen.pid:獲取子進程的進程ID。 11.Popen.returncode:獲取進程的返回值。如果進程還沒有結束,返回None。 12.subprocess.call(*popenargs, **kwargs):運行命令。該函數將一直等待到子進程運行結束,並返回進程的returncode。文章一開始的例子就演示了call函數。如果子進程不需要進行交互,就可以使用該函數來創建。 13.subprocess.check_call(*popenargs, **kwargs):與subprocess.call(*popenargs, **kwargs)功能一樣,只是如果子進程返回的returncode不為0的話,將觸發CalledProcessError異常。在異常對象中,包括進程的returncode信息。
子進程的PID存儲在child.pid
Popen對象 Popen對象有以下方法: Popen.poll() 檢查子進程是否已結束,設置並返回 returncode 屬性。 Popen.wait() 等待子進程結束,設置並返回 returncode 屬性。 注意:如果子進程輸出了大量數據到stdout或者stderr的管道,並達到了系統 pipe的緩存大小的話,子進程會等待父進程讀取管道,而父進程此時正wait着的話,將會產生傳說中的死鎖,后果是非常嚴重滴。建議使用communicate()來 避免這種情況的發生。 Popen.communicate(input=None) 和子進程交互:發送數據到stdin,並從stdout和stderr讀數據,直到收到EOF。等待子進程結束。可選的input如有 有的話,要為字符串類型。 此函數返回一個元組: (stdoutdata, stderrdata) 。 注意,要給子進程的stdin發送數據,則Popen的時候,stdin要為PIPE;同理,要可以收數據的話,stdout或者stderr也要為 PIPE。 注意:讀到的數據會被緩存在內存里,所以數據量非常大的時候要小心了。 Popen.send_signal(signal) 給子進程發送signal信號量。 注意:windows下目前只支持發送SIGTERM,等效於下面的terminate()。 Popen.terminate() 停止子進程。Posix下是發送SIGTERM信號。windows下是調用TerminateProcess()這 個API。 Popen.kill() 殺死子進程。Posix下是發送SIGKILL信號。windows下和terminate()無異。 Popen.stdin 如果stdin參數是PIPE,此屬性就是一個文件對象,否則為None。 Popen.stdout 如果stdout參數是PIPE,此屬性就是一個文件對象,否則為None。 Popen.stderr 如果stderr參數是PIPE,此屬性就是一個文件對象,否則為None。 Popen.pid 子進程的進程號。注意,如果shell參數為True,這屬性指的是子shell的進程號。 Popen.returncode 子程序的返回值,由poll()或者wait()設置,間接地也由communicate()設置。 如果為None,表示子進程還沒終止。 如果為負數-N的話,表示子進程被N號信號終止。(僅限*nux)
http://blog.csdn.net/mr_jj_lian/article/details/6936984
3. 子進程的文本流控制
(沿用child子進程) 子進程的標准輸入,標准輸出和標准錯誤也可以通過如下屬性表示:
child.stdin
child.stdout
child.stderr
我們可以在Popen()建立子進程的時候改變標准輸入、標准輸出和標准錯誤,並可以利用subprocess.PIPE將多個子進程的輸入和輸出連接在一起,構成管道(pipe):
1 #!/usr/bin/env python 2 3 import subprocess 4 5 child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) 6 child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE) 7 out = child2.communicate() 8 print out
child1.stdout-->subprocess.PIPE
child2.stdin<--subprocess.PIPE
child2.stdout-->subprocess.PIPE
相當於將child1.stdout-->child2.stdin->child2.stdout->subprocess.PIPE
subprocess.PIPE實際上為文本流提供一個緩存區。child1的stdout將文本輸出到緩存區,隨后child2的stdin從該PIPE中將文本讀取走。child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
要注意的是,communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成。
我們還可以利用communicate()方法來使用PIPE給子進程輸入:
1 import subprocess 2 child = subprocess.Popen(["cat"], stdin=subprocess.PIPE) 3 child.communicate("vamei") //()不為空,則寫入subprocess.PIPE,為空,則從subprocess.PIPE讀取
subprocess.PIPE-->child.stdin
commiuncate相當於寫入subprocess.PIPE,然后child從subprocess.PIPE讀取
- returnCode
執行子進程后的返回值是從何而來呢?通過exit的返回值得到
1 #!/bin/bash 2 3 echo "hello" 4 exit 1 5 ~
1 #!/usr/bin/env python 2 3 import subprocess 4 5 child = subprocess.Popen(["./shell.sh"], stdout=subprocess.PIPE) 6 returnCode = child.wait() 7 print "returnCode:",returnCode 8 stdout = child.communicate() 9 print stdout
yca@ubuntu:~/Desktop/go$ ./assert.py returnCode: 1 ('hello\n', None)