Подпроцесс 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='')
Замечания:
sys.exectable
текущий исполняемый файл Python (в данном случае Python 3)universal_newlines=True
включает текстовый режим (в противном случае,cat.stdin
а такжеcat.stdout
работа с байтами, а не со строками)-u
делает дочерний вывод буферизованным в строке ( иначе вы ничего не увидите, пока ребенок не очистит свой внутренний буфер stdout)-
with
-statement закрывает каналы и ожидает завершения дочернего процесса.
И вот соответствующий 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)
Вывод такой же (как показано выше).