標籤:
轉 http://blog.csdn.net/imzoer/article/details/8678029
subprocess的目的就是啟動一個新的進程並且與之通訊。
subprocess模組中只定義了一個類: 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)
參數args可以是字串或者序列類型(如:list,元組),用於指定進程的可執行檔及其參數。如果是序列類型,第一個元素通常是可執行檔的路徑。我們也可以顯式的使用executeable參數來指定可執行檔的路徑。
參數stdin, stdout, stderr分別表示程式的標準輸入、輸出、錯誤控制代碼。他們可以是PIPE,檔案描述符或檔案對象,也可以設定為None,表示從父進程繼承。
如果參數shell設為true,程式將通過shell來執行。
參數env是字典類型,用於指定子進程的環境變數。如果env = None,子進程的環境變數將從父進程中繼承。
subprocess.PIPE
在建立Popen對象時,subprocess.PIPE可以初始化stdin, stdout或stderr參數。表示與子進程通訊的標準流。
subprocess.STDOUT
建立Popen對象時,用於初始化stderr參數,表示將錯誤通過標準輸出資料流輸出。
Popen的方法:
Popen.poll()
用於檢查子進程是否已經結束。設定並返回returncode屬性。
Popen.wait()
等待子進程結束。設定並返回returncode屬性。
Popen.communicate(input=None)
與子進程進行互動。向stdin發送資料,或從stdout和stderr中讀取資料。選擇性參數input指定發送到子進程的參數。Communicate()返回一個元組:(stdoutdata, stderrdata)。注意:如果希望通過進程的stdin向其發送資料,在建立Popen對象的時候,參數stdin必須被設定為PIPE。同樣,如果希望從stdout和stderr擷取資料,必須將stdout和stderr設定為PIPE。
Popen.send_signal(signal)
向子進程發送訊號。
Popen.terminate()
停止(stop)子進程。在windows平台下,該方法將調用Windows API TerminateProcess()來結束子進程。
Popen.kill()
殺死子進程。
Popen.stdin,Popen.stdout ,Popen.stderr ,官方文檔上這麼說:
stdin, stdout and stderr specify the executed programs’ standard input, standard output and standard error file handles, respectively. Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.
Popen.pid
擷取子進程的進程ID。
Popen.returncode
擷取進程的傳回值。如果進程還沒有結束,返回None。
---------------------------------------------------------------
簡單的用法:
[python] view plaincopy
- p=subprocess.Popen("dir", shell=True)
- p.wait()
shell參數根據你要執行的命令的情況來決定,上面是dir命令,就一定要shell=True了,p.wait()可以得到命令的傳回值。
如果上面寫成a=p.wait(),a就是returncode。那麼輸出a的話,有可能就是0【表示執行成功】。
---------------------------------------------------------------------------
進程通訊
如果想得到進程的輸出,管道是個很方便的方法,這樣:
[python] view plaincopy
- p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdoutput,erroutput) = p.<span>commu</span>nicate()
p.communicate會一直等到進程退出,並將標準輸出和標準錯誤輸出返回,這樣就可以得到子進程的輸出了。
再看一個communicate的例子。
上面的例子通過communicate給stdin發送資料,然後使用一個tuple接收命令的執行結果。
------------------------------------------------------------------------
上面,標準輸出和標準錯誤輸出是分開的,也可以合并起來,只需要將stderr參數設定為subprocess.STDOUT就可以了,這樣子:
[python] view plaincopy
- p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- (stdoutput,erroutput) = p.<span>commu</span>nicate()
如果你想一行行處理子進程的輸出,也沒有問題:
[python] view plaincopy
- p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- while True:
- buff = p.stdout.readline()
- if buff == ‘‘ and p.poll() != None:
- break
------------------------------------------------------
死結
但是如果你使用了管道,而又不去處理管道的輸出,那麼小心點,如果子進程輸出資料過多,死結就會發生了,比如下面的用法:
[python] view plaincopy
- p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- p.wait()
longprint是一個假想的有大量輸出的進程,那麼在我的xp, Python2.5的環境下,當輸出達到4096時,死結就發生了。當然,如果我們用p.stdout.readline或者p.communicate去清理輸出,那麼無論輸出多少,死結都是不會發生的。或者我們不使用管道,比如不做重新導向,或者重新導向到檔案,也都是可以避免死結的。
----------------------------------
subprocess還可以串連起來多個命令來執行。
在shell中我們知道,想要串連多個命令可以使用管道。
在subprocess中,可以使用上一個命令執行的輸出結果作為下一次執行的輸入。例子如下:
例子中,p2使用了第一次執行命令的結果p1的stdout作為輸入資料,然後執行tail命令。
- -------------------
下面是一個更大的例子。用來ping一系列的ip地址,並輸出是否這些地址的主機是alive的。代碼參考了python unix linux 系統管理指南。
[python] view plaincopy
- #!/usr/bin/env python
-
- from threading import Thread
- import subprocess
- from Queue import Queue
-
- num_threads=3
- ips=[‘127.0.0.1‘,‘116.56.148.187‘]
- q=Queue()
- def pingme(i,queue):
- while True:
- ip=queue.get()
- print ‘Thread %s pinging %s‘ %(i,ip)
- ret=subprocess.call(‘ping -c 1 %s‘ % ip,shell=True,stdout=open(‘/dev/null‘,‘w‘),stderr=subprocess.STDOUT)
- if ret==0:
- print ‘%s is alive!‘ %ip
- elif ret==1:
- print ‘%s is down...‘%ip
- queue.task_done()
-
- #start num_threads threads
- for i in range(num_threads):
- t=Thread(target=pingme,args=(i,q))
- t.setDaemon(True)
- t.start()
-
- for ip in ips:
- q.put(ip)
- print ‘main thread waiting...‘
- q.join();print ‘Done‘
在上面代碼中使用subprocess的主要好處是,使用多個線程來執行ping命令會節省大量時間。
假設說我們用一個線程來處理,那麼每個 ping都要等待前一個結束之後再ping其他地址。那麼如果有100個地址,一共需要的時間=100*平均時間。
如果使用多個線程,那麼最長執行時間的線程就是整個程式啟動並執行總時間。【時間比單個線程節省多了】
這裡要注意一下Queue模組的學習。
pingme函數的執行是這樣的:
啟動的線程會去執行pingme函數。
pingme函數會檢測隊列中是否有元素。如果有的話,則取出並執行ping命令。
這個隊列是多個線程共用的。所以這裡我們不使用列表。【假設在這裡我們使用列表,那麼需要我們自己來進行同步控制。Queue本身已經通過訊號量做了同步控制,節省了我們自己做同步控制的工作=。=】
代碼中q的join函數是阻塞當前線程。下面是e文注釋
Queue.join()
Blocks until all items in the queue have been gotten and processed(task_done()).
---------------------------------------------
學習Processing模組的時候,遇到了進程的join函數。進程的join函數意思說,等待進程運行結束。與這裡的Queue的join有異曲同工之妙啊。processing模組學習的文章在這裡
subprocess.call(*popenargs, **kwargs)
Run command with arguments. Wait for command to complete, then return the returncode attribute
subprocess.check_call(*popenargs, **kwargs)
Run command with arguments. Wait for command to complete. If the exit code was zero then return, otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute.
python subprocess模組