Python在保留顺序的同时分别从子进程stdout和stderr中读取


问题内容

我有一个python子进程,我正在尝试从中读取输出和错误流。目前,我可以使用它,但是我只能从中读取stderr完之后才能读取stdout。看起来是这样的:

process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_iterator = iter(process.stdout.readline, b"")
stderr_iterator = iter(process.stderr.readline, b"")

for line in stdout_iterator:
    # Do stuff with line
    print line

for line in stderr_iterator:
    # Do stuff with line
    print line

如您所见,stderrfor循环要等到stdout循环完成才能开始。我如何修改它以便能够以正确的顺序从行中读取两者?

*需要 *说明的是: 我仍然需要能够判断行是来自stdout还是stderr因为在我的代码中对它们的区别对待。


问题答案:

这是一种基于的解决方案selectors,但可以保留顺序,并流式传输可变长度字符(甚至是单个字符)。

诀窍是使用read1()而不是read()

import selectors
import subprocess
import sys

p = subprocess.Popen(
    ["python", "random_out.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

sel = selectors.DefaultSelector()
sel.register(p.stdout, selectors.EVENT_READ)
sel.register(p.stderr, selectors.EVENT_READ)

while True:
    for key, _ in sel.select():
        data = key.fileobj.read1().decode()
        if not data:
            exit()
        if key.fileobj is p.stdout:
            print(data, end="")
        else:
            print(data, end="", file=sys.stderr)

如果您需要测试程序,请使用它。

import sys
from time import sleep


for i in range(10):
    print(f" x{i} ", file=sys.stderr, end="")
    sleep(0.1)
    print(f" y{i} ", end="")
    sleep(0.1)