2011-09-30 74 views
3

我已經閱讀了許多與此有關的問題,並學到了很多,但我仍然無法解決我的問題。我正在構建一個運行C++可執行文件的wxPython應用程序,並實時顯示該可執行文件的stdout。我遇到了幾個奇怪的結果,試圖使這項工作。這裏是我的當前設置/問題:Python從子流程捕獲標準輸出逐行

//test.cc (compiled as test.out with gcc 4.5.2) 
#include <stdio.h> 
int main() 
{ 
    FILE* fh = fopen("output.txt", "w"); 
    for (int i = 0; i < 10000; i++) 
    { 
     printf("Outputting: %d\n", i); 
     fprintf(fh, "Outputting: %d\n", i); 
    } 
    fclose(fh); 
    return 0; 
} 

#wxPythonScript.py (running on 2.7 interpreter) 
def run(self): 
    self.externalBinary = subprocess.Popen(['./test.out'], shell=False, stdout=subprocess.PIPE) 
    while not self.wantAbort: 
     line = self.externalBinary.stdout.readline() 
     wx.PostEvent(self.notifyWindow, Result_Event(line, Result_Event.EVT_STDOUT_ID)) 
    print('Subprocess still running') 
    print('Subprocess aborted smoothly') 

如果我運行上面的代碼的子進程需要很長的時間才能完成,即使所有它需要做的就是寫出來的數據並退出。但是,如果我運行下面的它很快就結束了:

#wxPythonScript.py (running on 2.7 interpreter) 
def run(self): 
    outFile = open('output.txt', 'r+') 
    self.externalBinary = subprocess.Popen(['./test.out'], shell=False, stdout=outFile) 
    while not self.wantAbort: 
     #line = self.externalBinary.stdout.readline() 
     #wx.PostEvent(self.notifyWindow, Result_Event(line, Result_Event.EVT_STDOUT_ID)) 
    print('Subprocess still running') 
    print('Subprocess aborted smoothly') 

所以基本上每當我從子到管道標準輸出重定向它會減慢/掛起,但如果我把它寫入一個文件,或不把它重定向完全沒問題。這是爲什麼?

+0

請搜索。這經常被問到。 –

+0

可能的重複[讀取子進程標準輸出行](http://stackoverflow.com/questions/2804543/read-subprocess-stdout-line-by-line) –

+0

我做過承諾。我讀過一噸。 2天前,我對子進程,線程,wx事件,stdout一無所知。我會繼續尋找當然。你能推薦一些關鍵字來搜索嗎?也許我在尋找錯誤的東西。大多數我搜查:非阻塞stdout.readline python,子進程標準輸出等 – anderspitman

回答

6

我只測試在Windows操作系統上,但它在2.6.6工作,2.7.2,3.2.1和:

from __future__ import print_function 
from subprocess import PIPE, Popen 
from threading import Thread 
import sys 

try: 
    from Queue import Queue, Empty 
except ImportError: 
    from queue import Queue, Empty # python 3.x 

ON_POSIX = 'posix' in sys.builtin_module_names 

def enqueue_output(out, queue): 
    for line in iter(out.readline, b''): 
     line = line.decode(sys.stdout.encoding) 
     queue.put(line) 
    out.close() 

def main(): 
    p = Popen(['c/main.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX) 
    q = Queue() 
    t = Thread(target=enqueue_output, args=(p.stdout, q)) 
    t.daemon = True # thread dies with the program 
    t.start() 

    #initially the queue is empty and stdout is open 
    #stdout is closed when enqueue_output finishes 
    #then continue printing until the queue is empty 

    while not p.stdout.closed or not q.empty(): 
     try: 
      line = q.get_nowait() 
     except Empty: 
      continue 
     else: 
      print(line, end='') 
    return 0 

if __name__ == '__main__': 
    sys.exit(main()) 

輸出:

Outputting: 0 
Outputting: 1 
Outputting: 2 
... 
Outputting: 9997 
Outputting: 9998 
Outputting: 9999 

編輯:

readline()會阻塞,直到程序的stdout緩衝區刷新,如果數據流是間歇性的,則可能需要很長時間。如果您可以編輯源代碼,則可以選擇手動調用fflush(stdout),或者在程序啓動時使用setvbuf來禁用緩衝。例如:

#include <stdio.h> 

int main() { 

    setvbuf(stdout, NULL, _IONBF, 0); 

    FILE* fh = fopen("output.txt", "w"); 
    int i; 

    for (i = 0; i < 10; i++) { 
     printf("Outputting: %d\n", i); 
     fprintf(fh, "Outputting: %d\n", i); 
     sleep(1); 
    } 

    fclose(fh); 
    return 0; 
} 

還應考慮使用unbufferstdbuf修改現有的程序的輸出流。

+0

不錯,我認爲我們正在接近。如果我運行的所有東西都是有效的。然而,我需要調用的實際C程序非常緩慢地輸出數據(大約每秒1行)。如果我加入睡眠(1);到C循環它不輸出任何東西,除非我也添加一個fflush(stdout);我有一種感覺,這會讓某人比我更聰明,知道什麼是錯的。另外,我們假設我無法訪問C程序的源代碼,只需添加fflush命令即可。 – anderspitman

+0

這工作對我來說,當我加入了out.flush()後,右線= line.decode(sys.stdout.encoding)。我認爲我遇到的一個基本問題是,我已經有一個單獨的線程,以便我的GUI不會鎖定,但是我需要另一個線程來處理stdout。有沒有辦法在一個線程中處理讀取標準輸出?我仍然不明白髮生了什麼事情,這幾乎讓我感到不適,但它確實有用,所以感謝您的幫助。另外,close_fds做什麼? python文檔讓我更困惑。 – anderspitman

+0

沒關係我錯了。除非我直接在C代碼中刷新,否則它仍然無法工作。有任何想法嗎?看起來這應該是非常簡單的。輸出被放入標準輸出緩衝區。爲什麼我不能把它從緩衝區中拉出來,每個人都開心呢?你認爲在無緩衝模式下有一個錯誤嗎? – anderspitman