Подпроцесс Python несколько stdin.write и stdout.read

Спасибо, что нашли время ответить на вопрос. Я играю с Python 3.4, и у меня есть две простые программы на Python. Одна - программа test.py, которая принимает пользовательский ввод и что-то печатает.

while True:
    print("enter something...")
    x = input()
    print(x)
    time.sleep(1)

Чтобы отправить входные данные для этой программы, у меня есть другая программа, которая использует подпроцесс:

from subprocess import Popen, PIPE

cat = Popen('python test.py', shell=True, stdin=PIPE, stdout=PIPE)
cat.stdin.write("hello, world!\n")
cat.stdin.flush()
print(cat.stdout.readline())

cat.stdin.write("and another line\n")
cat.stdin.flush()
print(cat.stdout.readline())

Однако, когда я запускаю вышеуказанную программу, я получаю сообщение об ошибке:

enter something...

hello, world!
Traceback (most recent call last):
  File "/opt/test.py", line 9, in <module>
    x = input()
EOFError: EOF when reading a line
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

И если я заменю test.py стандартной командой linux, такой как 'cat', все будет работать как положено.

Есть ли способ отправить несколько записей стандартного ввода и прочитать несколько назад стандартный вывод?

1 ответ

Решение

В общем, вы должны использовать pexpect для интерактивных программ (диалоговые взаимодействия).

Ваша конкретная проблема может быть вызвана несоответствием версии Python (вы думаете, что ваш код выполняется с использованием Python 3, в то время как на самом деле он может выполняться с использованием Python 2). Второй выпуск (EOFError) ожидается: либо перехватите его в дочернем скрипте, либо предоставьте сигнал для выхода из дочернего процесса (для этого я использую пустую строку в примере кода ниже).

Вот код Python 3, который громко терпит неудачу на Python 2:

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

with Popen([sys.executable, '-u', 'test.py'], stdin=PIPE, stdout=PIPE,
           universal_newlines=True, bufsize=1) as cat:
    for input_string in ["hello, world!", "and another line", ""]:
        print(input_string, file=cat.stdin, flush=True)
        print(cat.stdout.readline(), end='')

Замечания:

И вот соответствующий test.py:

#!/usr/bin/env python3
import time

while True:
    x = input("enter something...")
    if not x: # exit if the input is empty
        break
    print(x)
    time.sleep(1)

Выход

enter something...hello, world!
enter something...and another line
enter something...

Примечание: после новой строки нет "enter something..."

Это работает, но это хрупко, читайте Q: Почему бы просто не использовать канал (popen())? и использовать pexpect вместо.


Если входные данные конечны и не зависят от выходных данных, вы можете передать все сразу:

#!/usr/bin/env python3
import sys
from subprocess import check_output

output = check_output([sys.executable, 'test.py'],
                      input="\n".join(["hello, world!", "and another line"]),
                      universal_newlines=True)
print(output, end='')

Эта версия требует, чтобы ребенок правильно обрабатывал EOF:

#!/usr/bin/env python3
import time

while True:
    try:
        x = input("enter something...")
    except EOFError:
        break # no more input

    print(x)
    time.sleep(1)

Вывод такой же (как показано выше).

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