2011-03-30 3 views
10

파이썬으로 외부 명령 행 프로그램을로드하고 파이프를 통해 통신하려고합니다. progam은 stdin을 통해 텍스트 입력을 받고 stdout에 줄 단위로 텍스트 출력을 생성합니다. 통신은 select()를 사용하여 비동기 적이어야합니다.Python : select()가 파이프의 모든 입력을 알리지 않습니다.

문제는 프로그램의 모든 출력이 select()에 신호되지 않는다는 것입니다. 일반적으로 마지막 한두 줄의 신호가 보내지지 않습니다. 만약 select()가 타임 아웃을 리턴하고 파이프에서 읽으 려한다면 readline()은 프로그램에서 보낸 라인을 즉시 반환한다. 아래 코드를 참조하십시오.

프로그램이 출력을 버퍼링하지 않고 모든 출력을 텍스트 줄로 보냅니다. 다른 많은 언어와 환경에서 파이프를 통해 프로그램에 연결하는 것은 지금까지 잘 작동했습니다.

Mac OSX 10.6에서 Python 3.1 및 3.2를 사용해 보았습니다. 내부적으로 file.readlines([size]) 루프와는 size의 내부 버퍼를 채우기 위해 시도 번 이상 read() 콜 더 호출

import subprocess 
import select 

engine = subprocess.Popen("Engine", bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
engine.stdin.write(b"go\n") 
engine.stdin.flush() 

while True: 
    inputready,outputready,exceptready = select.select([engine.stdout.fileno()] , [], [], 10.0) 

    if (inputready, outputready, exceptready) == ([], [], []): 
     print("trying to read from engine anyway...") 
     line = engine.stdout.readline() 
     print(line) 

    for s in inputready: 
     line = engine.stdout.readline() 
     print(line) 

답변

16

참고. read()에 대한 첫 번째 호출은 select()가 fd를 읽을 수 있다고 표시 했으므로 즉시 반환됩니다. 그러나 두 번째 호출은 데이터를 사용할 수있을 때까지 차단되어 select를 사용하는 목적을 무효화합니다. 어쨌든 file.readlines([size])을 비동기식 앱에 사용하는 것은 까다 롭습니다.

select를 통과 할 때마다 각 fd마다 os.read(fd, size) 번씩 전화해야합니다. 비 차단 읽기를 수행하고 데이터가 사용 가능할 때까지 부분 행을 버퍼링하고 EOF를 명확하게 감지 할 수 있습니다.

os.read을 사용하여 설명하기 위해 코드를 수정했습니다. 또한 'stderr 과정에서 읽습니다.

import os 
import select 
import subprocess 
from cStringIO import StringIO 

target = 'Engine' 
PIPE = subprocess.PIPE 
engine = subprocess.Popen(target, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE) 
engine.stdin.write(b"go\n") 
engine.stdin.flush() 

class LineReader(object): 

    def __init__(self, fd): 
     self._fd = fd 
     self._buf = '' 

    def fileno(self): 
     return self._fd 

    def readlines(self): 
     data = os.read(self._fd, 4096) 
     if not data: 
      # EOF 
      return None 
     self._buf += data 
     if '\n' not in data: 
      return [] 
     tmp = self._buf.split('\n') 
     lines, self._buf = tmp[:-1], tmp[-1] 
     return lines 

proc_stdout = LineReader(engine.stdout.fileno()) 
proc_stderr = LineReader(engine.stderr.fileno()) 
readable = [proc_stdout, proc_stderr] 

while readable: 
    ready = select.select(readable, [], [], 10.0)[0] 
    if not ready: 
     continue 
    for stream in ready: 
     lines = stream.readlines() 
     if lines is None: 
      # got EOF on this stream 
      readable.remove(stream) 
      continue 
     for line in lines: 
      print line 
+0

완벽하게 작동합니다. 감사합니다. – StefanMK

+0

기꺼이 도와 드리겠습니다! – samplebias

관련 문제