Python subprocess.Popen communicate() 和wait()使用上的區別,pythonpopenwait
之所以會糾結到這個問題上是因為發現在調用Popen的wait方法之後程式一直沒有返回。google發現wait是有可能產生死結的。為了把這個問題徹底弄清楚,搜尋一些資料過來看看:
原文連結:http://blog.csdn.net/carolzhang8406/article/details/22286913
看到別人的例子:
今天遇到的一個問題。簡單說就是,使用 subprocess
模組的 Popen
調用外部程式,如果 stdout
或 stderr
參數是 pipe,並且程式輸出超過作業系統的 pipe size時,如果使用 Popen.wait()
方式等待程式結束擷取傳回值,會導致死結,程式卡在 wait()
調用上。
ulimit -a
看到的 pipe size 是 4KB,那隻是每頁的大小,查詢得知 linux 預設的 pipe size 是 64KB。
看例子:
#!/usr/bin/env python# coding: utf-8# yc@2013/04/28import subprocessdef test(size): print 'start' cmd = 'dd if=/dev/urandom bs=1 count=%d 2>/dev/null' % size p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) #p.communicate() p.wait() print 'end'# 64KBtest(64 * 1024)# 64KB + 1Btest(64 * 1024 + 1)
首先測試輸出為 64KB 大小的情況。使用 dd 產生了正好 64KB 的標準輸出,由 subprocess.Popen
調用,然後使用 wait()
等待 dd
調用結束。可以看到正確的 start
和 end
輸出;然後測試比 64KB 多的情況,這種情況下只輸出了 start
,也就是說程式執行卡在了 p.wait()
上,程式死結。具體輸出如下:
startendstart
那死結問題如何避免呢?官方文檔裡推薦使用 Popen.communicate()
。這個方法會把輸出放在記憶體,而不是管道裡,所以這時候上限就和記憶體大小有關了,一般不會有問題。而且如果要獲得程式傳回值,可以在調用 Popen.communicate()
之後取 Popen.returncode
的值。
結論:如果使用 subprocess.Popen
,就不使用 Popen.wait()
,而使用 Popen.communicate()
來等待外部程式執行結束。
Popen.wait()¶
Wait for child process to terminate. Set and returnreturncode attribute.
Warning
This will deadlock when using stdout=PIPE and/orstderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.
-
Popen.communicate(
input=None)¶
-
Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optionalinput argument should be a string to be sent to the child process, orNone, if no data should be sent to the child.
communicate() returns a tuple (stdoutdata, stderrdata).
Note that if you want to send data to the process’s stdin, you need to create the Popen object with stdin=PIPE. Similarly, to get anything other thanNone in the result tuple, you need to give stdout=PIPE and/orstderr=PIPE too.
Note
The data read is buffered in memory, so do not use this method if the data size is large or unlimited.
subprocess 的兩種方法:
1)如果想調用之後直接阻塞到子程式調用結束:
Depending on how you want to work your script you have two options. If you want the commands to block and not do anything while it is executing, you can just use subprocess.call
.
#start and block until donesubprocess.call([data["om_points"], ">", diz['d']+"/points.xml"])
2)非阻塞的時候方式:
If you want to do things while it is executing or feed things into stdin
, you can use communicate
after the popen
call.
#start and process things, then waitp = subprocess.Popen(([data["om_points"], ">", diz['d']+"/points.xml"])print "Happens while running"p.communicate() #now wait
As stated in the documentation, wait
can deadlock, so communicate is advisable.