2012-01-23 128 views
10

有沒有辦法在python「持久化」中進行子進程調用?我正在調用需要一段時間才能加載多次的程序。所以,如果我能將這個程序打開並與它通信而不會造成它的損壞,那將是非常好的。持久性python子進程

我的Python腳本的卡通版本是這樣的:

for text in textcollection: 
    myprocess = subprocess.Popen(["myexecutable"], 
       stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
       stderr = None) 
    myoutputtext, err = myprocess.communicate(input=text) 

我需要分別處理每個文本,所以它所有加入到一個大的文本文件,一旦處理它不是一個選項。

最好,如果有這樣的

myprocess = subprocess.Popen(["myexecutable"], 
      stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
      stderr = None) for text in textcollection: 
for text in textcollection: 
    myoutputtext, err = myprocess.communicate(input=text) 

一種選擇,我可以離開過程公開,我會很感激。

回答

24

你可以使用myprocess.stdin.write()myprocess.stdout.read()與您的子進程進行通信,您只需要小心,以確保正確處理緩衝以防止阻止您的呼叫。

如果您的子過程的輸出定義良好,您應該能夠使用行緩衝和myprocess.stdout.readline()進行可靠的通信。

下面是一個例子:

>>> p = subprocess.Popen(['cat'], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
>>> p.stdin.write('hello world\n') 
>>> p.stdout.readline() 
'hello world\n' 
>>> p.stdout.readline()  # THIS CALL WILL BLOCK 

這種方法適用於Unix的替代方法是把文件句柄在非阻塞模式,這將使你打電話的功能,如myprocess.stdout.read()並有如果返回數據任何可用,或引發IOError如果沒有任何數據:

>>> p = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
>>> import fcntl, os 
>>> fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) 
0 
>>> p.stdout.read()   # raises an exception instead of blocking 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
IOError: [Errno 11] Resource temporarily unavailable 

這將允許你做這樣的事情:

fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) 
for text in textcollection: 
    myprocess.stdin.write(text + '\n') 
    while True: 
     myoutputtext = '' 
     try: 
      myoutputtext += myprocess.stdout.read() 
     except IOError: 
      pass 
     if validate_output(myoutputtext): 
      break 
     time.sleep(.1) # short sleep before attempting another read 

在本例中,validate_output()是您需要編寫的函數,如果您迄今爲止收到的數據全部是您期望得到的輸出,則返回True

+1

謝謝!我最喜歡你的解決方案,因爲它不需要第三方下載。不幸的是,它不適合我。在嘗試了幾件事情之後,我很確定這是我調用的Java程序而不是您的解決方案的問題,因此您的解決方案很好。 – JasonMond

+0

爲什麼選擇投票? –

+0

這是錯誤的。我的upvote是不活動的,直到編輯任何東西,但我沒有看到任何改善或不受傷害的東西。完美的答案。 – hynekcer

1

我認爲你正在尋找

myprocess.stdin.write(text) 

你可以創建Popens的列表,然後調用另一個循環的每個元素進行通信。 像這樣

processes=[] 
for text in textcollection: 
    myprocess = subprocess.Popen(["myexecutable"], 
       stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
       stderr = None) 
    myprocess.stdin.write(text) 
    processes.append(myprocess) 

for proc in processes: 
    myoutput, err=proc.communicate() 
    #do something with the output here 

這樣就不必等到所有的Popens已經開始

+0

不幸的是,這對我來說不起作用,因爲它是一個java程序,它在每次運行時都會消耗3G的內存。這就是爲什麼加載需要很長時間。我無法擁有5000個3G流程的實例。 – JasonMond

+0

我想我明白了。在獲得輸入文本後,它輸出一些內容然後退出?或者是否等待您輸入其他內容 –

+0

它輸出然後退出。 – JasonMond

5

後,這是communicate()電話是殺害你的子進程。按照subprocess documentationcommunicate()方法:

與互動的過程:將數據發送至標準輸入。從stdout和stderr中讀取數據,直到達到文件結尾。等待進程終止。

你想要做的是直接與POpen對象的stdinstdout性能直接與子進行交流互動。但是,文檔建議對此說法:

警告:使用通信()而不是.stdin.write,啓動並阻止子進程。

因此,您需要爲潛在的死鎖實施您自己的解決方法,或者希望有人爲您寫了asynchronous subprocess module

編輯:這裏有一個如何異步子模塊可以使用quick'n'dirty例如:

import asyncsubprocess 

textcollection = ['to', 'be', 'or', 'not', 'to be', 'that is the', 'question'] 

myprocess = asyncsubprocess.Popen(["cat"], 
    stdin = asyncsubprocess.PIPE, 
    stdout = asyncsubprocess.PIPE, 
    stderr = None) 

for text in textcollection: 
    bytes_sent, myoutput, err = myprocess.listen(text) 
    print text, bytes_sent, myoutput, err 

當我運行它,它打印:

to 2 to 
be 2 be 
or 2 or 
not 3 not 
to be 5 to be 
that is the 11 that is the 
question 8 question 
-2
if os.name == 'nt': 
startupinfo = subprocess.STARTUPINFO() 
startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW 
subprocess.call(os.popen(tempFileName), shell=True) 
os.remove(tempFileName)