1. 程式人生 > >python當中subprocess裡call與Popen的用法區別

python當中subprocess裡call與Popen的用法區別

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,stdoutandstderrspecify the executed programs’ standard input, standard output and standard error file handles, respectively. Valid values arePIPE, an existing file descriptor (a positive integer), an existing file object, andNone.

Popen.pid

  獲取子程序程序ID。

Popen.returncode

  獲取程序的返回值。如果程序還沒有結束,返回None。

---------------------------------------------------------------

簡單的用法:

[python] view plain copy print?
  1. p=subprocess.Popen("dir", shell=True)  
  2. p.wait()  
p=subprocess.Popen("dir", shell=True)
p.wait()

shell引數根據你要執行的命令的情況來決定,上面是dir命令,就一定要shell=True了,p.wait()可以得到命令的返回值。

如果上面寫成a=p.wait(),a就是returncode。那麼輸出a的話,有可能就是0【表示執行成功】。

---------------------------------------------------------------------------

程序通訊

如果想得到程序的輸出,管道是個很方便的方法,這樣:

[python] view plain copy print?
  1. p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  
  2. (stdoutput,erroutput) = p.<span>commu</span>nicate()  
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutput,erroutput) = p.communicate()

p.communicate會一直等到程序退出,並將標準輸出和標準錯誤輸出返回,這樣就可以得到子程序的輸出了。

再看一個communicate的例子。

上面的例子通過communicate給stdin傳送資料,然後使用一個tuple接收命令的執行結果。

------------------------------------------------------------------------

上面,標準輸出和標準錯誤輸出是分開的,也可以合併起來,只需要將stderr引數設定為subprocess.STDOUT就可以了,這樣子:

[python] view plain copy print?
  1. p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
  2. (stdoutput,erroutput) = p.<span>commu</span>nicate()  
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdoutput,erroutput) = p.communicate()

如果你想一行行處理子程序的輸出,也沒有問題:

[python] view plain copy print?
  1. p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
  2. whileTrue:  
  3.     buff = p.stdout.readline()  
  4.     if buff == ''and p.poll() != None:  
  5.         break
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 plain copy print?
  1. p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
  2. p.wait()  
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 unixLinux 系統管理指南。

[python] view plain copy print?
  1. #!/usr/bin/env python
  2. from threading import Thread  
  3. import subprocess  
  4. from Queue import Queue  
  5. num_threads=3
  6. ips=['127.0.0.1','116.56.148.187']  
  7. q=Queue()  
  8. def pingme(i,queue):  
  9.     whileTrue:  
  10.         ip=queue.get()  
  11.         print'Thread %s pinging %s' %(i,ip)  
  12.         ret=subprocess.call('ping -c 1 %s' % ip,shell=True,stdout=open('/dev/null','w'),stderr=subprocess.STDOUT)  
  13.         if ret==0:  
  14.             print'%s is alive!' %ip  
  15.         elif ret==1:  
  16.             print'%s is down...'%ip  
  17.         queue.task_done()  
  18. #start num_threads threads
  19. for i in range(num_threads):  
  20.     t=Thread(target=pingme,args=(i,q))  
  21.     t.setDaemon(True)  
  22.     t.start()  
  23. for ip in ips:  
  24.     q.put(ip)  
  25. print'main thread waiting...'
  26. q.join();print'Done'
#!/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模組學習的文章在這裡