我能想出幾個解決方案。
#1:你可以進入源代碼抓取the code for communicate
,複製並粘貼它,在代碼中添加打印每一行的代碼以及緩衝事件。 (如果你的stdout
因爲父母死鎖而可能被阻止,你可以使用threading.Queue
或者其他的東西)。這顯然有點不方便,但它很簡單,而且很安全。
但真的,communicate
是複雜的,因爲它需要是完全一般的,並處理你不需要的情況。所有你需要的是中心把戲:在問題上拋出線索。一個專門的讀卡器線程,不會做任何緩慢或阻塞read
調用之間的任何東西都是你需要的。
事情是這樣的:
self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE)
lines = []
def reader():
for line in self.process.stdout:
lines.append(line)
sys.stdout.write(line)
t = threading.Thread(target=reader)
t.start()
self.process.wait()
t.join()
你可能需要一些錯誤在reader
線程處理。我並不是100%確定您可以在這裏安全使用readline
。但是這將會工作,或者接近。
#2:或者您可以創建一個包裝類,每次有人從其中獲取文件對象和T恤stdout
/stderr
。然後手動創建管道,並傳入包裝管道,而不是使用automagic PIPE
。這與#1有完全相同的問題(意思是沒有問題,或者如果sys.stdout.write
可以阻止,則需要使用Queue
或其他內容)。
像這樣:
class TeeReader(object):
def __init__(self, input_file, tee_file):
self.input_file = input_file
self.tee_file = tee_file
def read(self, size=-1):
ret = self.input_file.read(size)
if ret:
self.tee_file.write(ret)
return ret
換句話說,它封裝了一個文件對象(或東西的作用就像一個),和作用就像一個文件對象。 (當您使用PIPE
時,process.stdout
在Unix上是一個真正的文件對象,但可能只是在Windows上的行爲。)您需要委託給input_file
的任何其他方法都可以直接進行委派,而無需任何額外的包裝。試試這個,看看communicate
得到AttributeException
什麼方法尋找和明確地編碼那些,或者做慣常的__getattr__
訣竅委託一切。 PS,如果你擔心這個「文件對象」的含義磁盤存儲的想法,請參閱維基百科的Everything is a file。 #3:最後,您可以在PyPI上抓取其中一個「async subprocess」模塊,或將其包含在twisted
或其他異步框架中,然後使用該模塊。 (這使得可能避免死鎖的問題,但它不是保證 - 你還是要確保服務管道正常。)
你將如何實現#2? '文件'對象表明我必須寫入磁盤(我不想)? – jaka 2013-03-27 08:32:11
@jaka:不,管道已經是一個文件對象。您正在創建一個包裝文件對象的對象,並將所有文件方法委託給它(除了'read',它在委託調用中會做一些額外的工作)。 – abarnert 2013-03-27 08:36:22
我會嘗試添加至少一些骨架代碼的前兩個答案。 – abarnert 2013-03-27 08:38:28