2013-03-27 114 views
3

目前,我有這樣的事情:如何捕獲流輸出Python從subprocess.communicate()

self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE) 
out, err = self.process.communicate() 

我跑的命令流輸出,我需要的過程,然後再繼續阻止。

我該如何做到這一點,以便我可以捕獲流式輸出並通過stdout打印流式輸出?當我設置stdout=subprocess.PIPE時,我可以捕獲輸出,但不會打印輸出。如果我遺漏了stdout=subprocess.PIPE,它將打印輸出,但communicate()將返回None

是否有解決方案,可以做我所要求的?儘管提供阻塞,直到進程終止/完成,避免緩衝區問題和管道死鎖問題提到here

謝謝!

回答

3

我能想出幾個解決方案。

#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或其他異步框架中,然後使用該模塊。 (這使得可能避免死鎖的問題,但它不是保證 - 你還是要確保服務管道正常。)

+0

你將如何實現#2? '文件'對象表明我必須寫入磁盤(我不想)? – jaka 2013-03-27 08:32:11

+0

@jaka:不,管道已經是一個文件對象。您正在創建一個包裝文件對象的對象,並將所有文件方法委託給它(除了'read',它在委託調用中會做一些額外的工作)。 – abarnert 2013-03-27 08:36:22

+0

我會嘗試添加至少一些骨架代碼的前兩個答案。 – abarnert 2013-03-27 08:38:28

1

輸出進入您的調用過程,基本上從self.cmd捕獲stdout,以便輸出不會去其他地方。

如果你想看到輸出,你需要做的是從'父'進程打印它。

+0

我敢肯定,整個的一點是,他想打印當它進來時 - 但是他的父進程被阻塞,直到「通信」返回,因此他不能從父進程打印它。 – abarnert 2013-03-27 08:10:54

+0

如何在使用'communic()'時捕獲它? – jaka 2013-03-27 08:24:33