2011-09-30 3 views
3

많은 질문을 읽었으며 많은 것을 배웠습니다.하지만 여전히 문제를 해결할 수 없었습니다. C++ 실행 파일을 실행하고 실행 파일에서 stdout을 실시간으로 표시하는 wxPython 앱을 빌드하고 있습니다. 이 작품을 만들려고 몇 가지 이상한 결과가 나타났습니다. 나는 서브 프로세스를 완료하는 데 시간이 오래 걸리는 위의 코드를 실행하면이 할 수있는 모든 데이터와 출구를 쓰는 경우에도파이썬은 라인 단위로 서브 프로세스에서 stdout을 캡쳐합니다.

//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') 

: 여기 내 현재 설정/문제입니다. 그러나 내가 실행하는 경우 매우 빠르게 완료된 다음 나는 PIPE에 서브 프로세스에서 표준 출력 리디렉션 때마다 다운/정지 속도가 느려 그래서 기본적으로

#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

[read subprocess stdout line by line] 가능한 복제본 (0120)을 참조하십시오. –

+0

약속 했어요. 나는 1 톤을 읽었다. 2 일 전 나는 서브 프로세스, 스레드, wx 이벤트, stdout에 대해 아무것도 몰랐습니다. 나는 물론 계속 찾고있을 것이다. 검색 할 키워드를 추천 해 주시겠습니까? 어쩌면 내가 잘못된 것을 찾고있을거야. 주로 검색했습니다 : stdout.readline python, subprocess stdout 등을 차단하지 않았습니다. – anderspitman

답변

6

I는 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)도 추가하지 않는 한; 나는 이것이 나보다 더 똑똑한 사람에게 무엇이 잘못되었는지에 대한 단서를 줄 것이라고 생각한다. 또한 단순히 fflush 명령을 추가하기 위해 C 프로그램의 소스에 액세스 할 수 없다고 가정 해 보겠습니다. – anderspitman

+0

line = line.decode (sys.stdout.encoding) 바로 뒤에 out.flush()를 추가하면 저에게 효과적입니다. 내가 가진 근본적인 문제 중 하나는 내 GUI가 잠기지 않도록 별도의 스레드를 이미 가지고 있지만 stdout을 처리하기 위해 다른 스레드가 필요하다는 것입니다. 한 스레드에서 stdout을 읽을 수있는 방법이 있습니까? 나는 아직도 무슨 일이 일어나고 있는지 이해하지 못한다. 그것은 작동하지 않는 것보다 나를 괴롭힌다. 하지만 효과가 있습니다. 도움을 주셔서 감사합니다. close_fds는 무엇을합니까? 파이썬 문서는 저를 더 혼란스럽게 만들었습니다. – anderspitman

+0

Nevermind 틀렸어. C 코드에서 직접 플러시하지 않으면 여전히 작동하지 않습니다. 어떤 아이디어? 이것은 매우 간단해야만합니다. 출력이 stdout 버퍼에 저장됩니다. 왜 그냥 버퍼에서 꺼내서 모든 사람들이 행복하지 않을까요? 버퍼링되지 않은 모드에 버그가 있다고 생각합니까? – anderspitman

관련 문제