Чтение в реальном времени из subprocess.stdout в Windows

Подчеркнем, что проблема заключается в чтении в реальном времени, а не в чтении без блокировки. Это было задано ранее, например, subprocess.Popen.stdout - чтение stdout в режиме реального времени (снова). Но удовлетворительного решения не было предложено.

В качестве примера, следующий код пытается симулировать оболочку python.

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. с помощью fctrl а также O_NONBLOCK
  2. с помощью thread а также queue

В то время как 1-й вариант может работать и работать только на Linux, 2-й вариант просто превращает блокирующее чтение в неблокирующее чтение, т.е. я не могу получить вывод подпроцесса в реальном времени. Например, если я введу ' print("hello") "Я ничего не получу от p.stdout используя 2-й раствор.

Возможно, кто-то посоветует 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())

Решение: Ниже приведен окончательный код, основанный на ответе и комментариях Дж. Ф. Себастьяна.

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 ответ

Решение

Вот полный рабочий пример, который использует подпроцесс в интерактивном режиме:

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

Вот еще один пример: как запустить [sys.executable, '-u', 'test.py'] в интерактивном режиме.

Другие вопросы по тегам