2011-03-30 17 views
10

से सभी इनपुट सिग्नल नहीं करता है। मैं पाइथन के साथ बाहरी कमांड लाइन प्रोग्राम लोड करने और पाइप के माध्यम से इसके साथ संवाद करने की कोशिश कर रहा हूं। प्रोगाम stdin के माध्यम से टेक्स्ट इनपुट लेता है और stdout के लिए लाइनों में पाठ आउटपुट पैदा करता है। चयन() का उपयोग कर संचार अतुल्यकालिक होना चाहिए।पायथन: चयन() पाइप

समस्या यह है कि कार्यक्रम के सभी आउटपुट को चयन() में संकेतित नहीं किया जाता है। आमतौर पर अंतिम एक या दो लाइनों को संकेत नहीं दिया जाता है। यदि एक टाइमआउट के साथ चयन() रिटर्न (और मैं पाइप से पढ़ने की कोशिश कर रहा हूं वैसे भी readline() प्रोग्राम से भेजे गए लाइन के साथ तत्काल लौटाता है। नीचे कोड देखें।

प्रोग्राम आउटपुट को बफर नहीं करता है और टेक्स्ट आउटपुट में सभी आउटपुट भेजता है। कई अन्य भाषाओं और वातावरण में पाइप के माध्यम से कार्यक्रम से जुड़ने से अब तक ठीक काम किया है।

मै मैक ओएसएक्स 10.6 पर पायथन 3.1 और 3.2 की कोशिश की है।

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

ध्यान दें कि आंतरिक रूप से file.readlines([size]) छोरों और read() syscall एक बार से अधिक का आह्वान, size का एक आंतरिक बफर को भरने के लिए प्रयास करते हैं। read() पर पहला कॉल तुरंत वापस आ जाएगा, क्योंकि चयन() इंगित करता है कि एफडी पठनीय था। हालांकि दूसरा कॉल तब तक अवरुद्ध होगा जब तक कि डेटा उपलब्ध न हो, जो चयन का उपयोग करने के उद्देश्य को हरा देता है। किसी भी मामले में एसिंक्रोनस ऐप में file.readlines([size]) का उपयोग करना मुश्किल है।

आपको चयन के माध्यम से प्रत्येक पास प्रत्येक एफडी पर os.read(fd, size) पर कॉल करना चाहिए। यह एक गैर-अवरुद्ध पढ़ने को निष्पादित करता है, और डेटा उपलब्ध होने तक आपको आंशिक रेखाओं को बफर करने देता है और ईओएफ को अनजाने में पहचानता है।

मैंने 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

संबंधित मुद्दे