Программы, предназначенные для взаимодействия с человеком, отличаются от программ, предназначенных для взаимодействия с другими программами. Ваш square.py
скрипт ближе к первой категории. Возможные проблемы:
- приглашение заканчивается в середине строки, которая заставляет родительский сценарий читать по одному байту за раз вместо полных строк или известных фрагментов, чтобы избежать блокировки
- ответы ребенка явно не сбрасываются. Это означает, что может быть включена буферизация блоков в неинтерактивном режиме например, когда он запускается через
subprocess
без pty
Вот как вы могли бы взаимодействовать с ним, используя модуль subprocess
в его текущей форме:
#!/usr/bin/env python
from __future__ import print_function
import sys
from itertools import cycle
from subprocess import Popen, PIPE
from textwrap import dedent
# start child process
p = Popen([sys.executable or 'python', '-u', '-c', dedent("""
for i in range(10):
x = int(input('Enter x-dimension: '))
print(x*x)
""")], stdin=PIPE, stdout=PIPE, universal_newlines=True, bufsize=1)
for n in cycle([3, 1, 4, 15, 926]): # infinite loop
while p.poll() is None: # while the subprocess is running
# send input to the child
print(n, file=p.stdin)
# read & parse answer
data = p.stdout.readline().rpartition(' ')[2]
if not data: # EOF
answer = None
break # exit inner loop
answer = int(data)
if answer == 1: # show example when input depends on output
n += 1
else: # done with given `n`
break # exit inner loop
else: # subprocess ended
break # exit outer loop
if answer is not None:
print("Input %3d Output %6d" % (n, answer))
p.communicate() # close pipes, wait for the child to terminate
И вот то же самое, но с использованием pexpect
(для сравнения):
#!/usr/bin/env python
import sys
from itertools import cycle
from textwrap import dedent
import pexpect
child = pexpect.spawnu(sys.executable or 'python', ['-c', dedent("""
for i in range(10):
x = int(input('Enter x-dimension: '))
print(x*x)
""")])
for n in cycle([3, 1, 4, 15, 926]):
while True:
i = child.expect([pexpect.EOF, u'x-dimension:'])
if i == 0: # EOF
answer = None
child.close()
sys.exit()
elif i == 1: # child waits for input
child.sendline(str(n))
child.expect(u'\\n\\d+\\s')
answer = int(child.after)
if answer == 1:
n += 1
else:
break
else:
assert 0
else: # child terminated
break
if answer is not None:
print("Input %3d Output %6d" % (n, answer))
Оба сценария написаны для поддержки Python 2 и Python 3 из одного источника.
Примечание: в сценарии на основе subprocess
есть аргумент -u
, который позволяет читать строки, как только они становятся доступными, даже в неинтерактивном режиме. Скрипт на основе pexpect
работает без такого переключателя. Программы на основе stdio
можно небуферизовать / сделать строчную буферизацию с помощью stdbuf
, unbuffer
утилит или путем предоставления pty.
Вы можете видеть, что даже самый простой дочерний сценарий (square.py
) требует решения нескольких проблем, чтобы вообще работать.
Все проще, когда дочерняя программа ожидает запуска из другой программы, оставаясь при этом доступной для чтения (отладки). В этом случае square.py
может выглядеть так:
#!/usr/bin/env python
import sys
import time
for line in iter(sys.stdin.readline, ''): # get line as soon as it is available
print(int(line)**2) # find square
sys.stdout.flush() # make the answer available immediately
time.sleep(.5) # a delay to show that the answer is available immediately
Его можно использовать из модуля на основе subprocess
в режиме «все сразу»:
import sys
from subprocess import Popen, PIPE
L = [2, 7, 1] # numbers to be squared
p = Popen([sys.executable or 'python', 'square.py'], stdin=PIPE, stdout=PIPE,
universal_newlines=True, bufsize=-1)
answers = map(int, p.communicate("\n".join(map(str, L)))[0].splitlines())
Или по одному номеру за раз:
#!/usr/bin/env python
import sys
from subprocess import Popen, PIPE
answers = []
p = Popen([sys.executable or 'python', 'square.py'], stdin=PIPE, stdout=PIPE,
bufsize=1)
for c in [b'2', b'7', b'1']:
p.stdin.write(c + b'\n')
p.stdin.flush()
answers.append(int(p.stdout.readline()))
print(answers)
p.communicate() # close pipes, wait for child to finish
print(answers)
Чтобы получить массив из вашей программы на C; вы могли json
модуль:
import json
from subprocess import Popen, PIPE
p = Popen(['./c-program', 'other', 'args'], stdin=PIPE, stdout=PIPE, bufsize=1)
p.stdin.write(json.dumps({'parameter': 8}).encode() + b'\n') # send input
p.stdin.flush()
result = json.loads(p.stdout.readline().decode()) # e.g., {"result": [0, 0, 7]}
# ...
p.communicate() # close pipes, wait for child to finish
person
jfs
schedule
25.11.2013