从Windows上的subprocess.stdout实时读取

时间:2016-04-27 03:24:57

标签: python windows subprocess output-buffering io-buffering

要强调的是,问题是实时阅读而不是非阻塞阅读。之前有人问过,例如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
  3. 虽然第一个可能工作并且只能在linux上工作,但第二个方法只是将阻塞读取变为非阻塞读取,即我无法获得子进程的实时输出。例如,如果我输入“print("hello")”,我将使用第二个解决方案从p.stdout获得任何内容。

    也许有人建议p.communite。不幸的是,它不适用于这种情况,因为它会按照here所描述的那样关闭标准输入。

    那么, 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. Sebastian的回答和评论的最终代码。

    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='')
    

    应该注意,当命令没有触发输出时程序会挂起。

1 个答案:

答案 0 :(得分:0)

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

#!/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='')

以下是另一个例子:how to run [sys.executable, '-u', 'test.py'] interactively