2011-09-30 153 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