简体   繁体   English

在 Python 中对子进程 PIPE 进行非阻塞读取,一次一个字节

[英]Non-blocking read on subprocess PIPE in Python, one byte at a time

I have implemented a variant on the code in this question:我在这个问题的代码上实现了一个变体:

A non-blocking read on a subprocess.PIPE in Python Python 中 subprocess.PIPE 的非阻塞读取

To try and read the output in real time from this dummy program test.py :要尝试从这个虚拟程序test.py实时读取输出:

import time, sys

print "Hello there"
for i in range(100):
    time.sleep(0.1)
    sys.stdout.write("\r%d"%i)
    sys.stdout.flush()
print
print "Go now or I shall taunt you once again!"

The variation on the other question is that the calling program must read character by character, not line by line, as the dummy program test.py outputs progress indication all on one line by use of \\r .另一个问题的变化是调用程序必须逐个字符而不是逐行读取,因为虚拟程序test.py使用\\r在一行上输出进度指示。 So here it is:所以这里是:

import sys,time
from subprocess import PIPE, Popen
from threading  import Thread

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):
    while True:
        buffersize = 1
        data = out.read(buffersize)
        if not data:
            break
        queue.put(data)
    out.close()

p = Popen(sys.executable + " test.py", 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()

while True:
    p.poll()
    if p.returncode:
        break
    # Read line without blocking
    try:
        char = q.get_nowait()
        time.sleep(0.1)
    except Empty:
        pass
    else: # Got line
        sys.stdout.write(char)
        sys.stdout.flush()

print "left loop"
sys.exit(0)

Two problems with this这有两个问题

  • It never exits - p.returncode never returns a value and the loop is not left.它永远不会退出 - p.returncode永远不会返回值,并且不会留下循环。 How can I fix it?我该如何解决?
  • It's really slow!真的很慢! Is there a way to make it more efficient without increasing buffersize ?有没有办法在不增加buffersize情况下提高效率?

As @Markku K. pointed out, you should use bufsize=0 to read one byte at a time.正如@Markku K. 指出的那样,您应该使用bufsize=0读取一个字节。

Your code doesn't require a non-blocking read.您的代码不需要非阻塞读取。 You can simplify it:你可以简化它:

import sys
from functools import partial
from subprocess import Popen, PIPE

p = Popen([sys.executable, "test.py"], stdout=PIPE, bufsize=0)
for b in iter(partial(p.stdout.read, 1), b""):
    print b # it should print as soon as `sys.stdout.flush()` is called
            # in the test.py
p.stdout.close()
p.wait()

Note: reading 1 byte at a time is very inefficient.注意:一次读取 1 个字节是非常低效的。

Also, in general, there could be a block-buffering issue that sometimes can be solved using pexpect , pty modules or unbuffer , stdbuf , script command-line utilities .此外,通常可能存在块缓冲问题,有时可以使用pexpectpty模块unbufferstdbufscript命令行实用程序来解决

For Python processes you could use -u flag to force unbuffering (binary layer) of stdin, stdout, stderr streams.对于 Python 进程,您可以使用-u标志来强制对 stdin、stdout、stderr 流进行非缓冲(二进制层)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM