2

この質問のコードにバリアントを実装しました。

Python の subprocess.PIPE でのノンブロッキング読み取り

このダミー プログラムからリアルタイムで出力を読み取ってみるには、次のようにし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!"

もう 1 つの質問のバリエーションは、ダミー プログラムtest.py\r. だからここにあります:

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)

これには2つの問題があります

  • 終了することはありません-p.returncode値を返すことはなく、ループは終了しません。どうすれば修正できますか?
  • 本当に遅いです!増やさずに効率化する方法はありbuffersizeますか?
4

1 に答える 1

2

@Markku K.が指摘したように、bufsize=0一度に1バイトずつ読み取るために使用する必要があります。

あなたのコードはノンブロッキング読み取りを必要としません。あなたはそれを単純化することができます:

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()

注: 一度に 1 バイトを読み取るのは非常に非効率的です。

また、一般にモジュールまたはコマンドライン ユーティリティ使用して解決できる場合があるブロック バッファリングの問題が存在する可能性があります。pexpectptyunbufferstdbufscript

Python プロセスの場合、-uフラグを使用して stdin、stdout、stderr ストリームのバッファリング解除 (バイナリ レイヤー) を強制できます。

于 2013-06-06T19:49:32.137 に答える