Python:select()不会发出来自管道的所有输入信号

时间:2011-03-30 13:09:32

标签: python select pipe

我正在尝试使用Python加载外部命令行程序并通过管道与它通信。 progam通过stdin获取文本输入并生成文本输出到stdout。使用select()将通信异步。

问题是,并非所有程序输出都在select()中发出信号。通常最后一行或两行不发信号。如果select()返回超时并且我试图从管道中读取,则readline()会立即返回程序发送的行。请参阅下面的代码。

程序不缓冲输出并以文本行发送所有输出。到目前为止,通过许多其他语言和环境的管道连接到程序已经很好了。

我在Mac OSX 10.6上尝试过Python 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)

1 个答案:

答案 0 :(得分:16)

请注意,内部file.readlines([size])循环并多次调用read()系统调用,尝试填充size的内部缓冲区。第一次调用read()将立即返回,因为select()表示fd是可读的。但是第二次调用将阻塞,直到数据可用,这违背了使用select的目的。在任何情况下,在异步应用程序中使用file.readlines([size])都很棘手。

每次通过选择时,您应该在每个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