Windows 从 subprocess.stdout 实时读取

Real time read from subprocess.stdout on Windows

强调一下,问题是实时读取而不是非阻塞读取。之前有人问过,例如subprocess.Popen.stdout - reading stdout in real-time (again)。但一直没有提出令人满意的解决方案。

例如,下面的代码试图模拟python shell。

import subprocess

p = subprocess.Popen(['python'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

while True:
    line = input('>>> ')
    p.stdin.write(line.encode())
    print('>>> ', p.stdout.read().decode())

但是,读取p.stdout时会被阻塞。经过四处寻找,我发现了以下两种可能的解决方案。

  1. using fctrl and O_NONBLOCK
  2. using thread and queue

虽然第一个解决方案可能工作并且只在 linux 上工作,第二个解决方案只是将阻塞读取转换为非阻塞读取,即我无法获得子进程的实时输出。例如,如果我输入“print("hello")”,使用第二种解决方案我将无法从 p.stdout 中得到任何结果。

也许有人会建议p.communite。不幸的是,它不适合这种情况,因为它会按照 .

所述关闭标准输入

那么,Windows有什么解决办法吗?

编辑:即使开启-u,将p.stdout.read替换为p.stdout.readline,问题依然存在

import subprocess

p = subprocess.Popen(['python', '-u'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

while True:
    line = input('>>> ')
    p.stdin.write(line.encode())
    p.stdin.flush()
    print('>>> ', p.stdout.readline().decode())

解决方法: 下面是基于J.F的最终代码。塞巴斯蒂安的回答和评论。

from subprocess import Popen, PIPE, STDOUT

with Popen(
        ['python', '-i', '-q'],
        stdin=PIPE, stdout=PIPE, stderr=STDOUT,
        bufsize=0
    ) as process:
    while True:
        line = input('>>> ')
        if not line:
            break
        process.stdin.write((line+'\n').encode())
        print(process.stdout.readline().decode(), end='')

需要注意的是,当命令触发无输出时,程序会挂起。

这是一个完整的工作示例,它以交互方式使用子进程:

#!/usr/bin/env python3
import sys
from subprocess import Popen, PIPE, DEVNULL

with Popen([sys.executable, '-i'], stdin=PIPE, stdout=PIPE, stderr=DEVNULL,
           universal_newlines=True) as process:
    for i in range(10):
        print("{}**2".format(i), file=process.stdin, flush=True)
        square = process.stdout.readline()
        print(square, end='')

这是另一个例子: