Я пытаюсь написать программу-питон, способную взаимодействовать с другими программами. Это означает отправку stdin и получение данных stdout. Я не могу использовать pexpect (хотя он определенно вдохновил часть дизайна). Процесс я использую прямо сейчас это:Использование подпроцесса с select и pty зависает при захвате вывода
- не Приложить PTY на стандартный вывод подпроцесса в
- Loop до выходов подпроцесса, проверяя
subprocess.poll
- Когда имеется в стандартном выводе пишет, что данные данные немедленно к текущему этапу.
- Отделка!
Я прототипировал какой-то код (ниже), который работает, но, похоже, имеет один изъян, который меня прослушивает. После завершения дочернего процесса родительский процесс зависает, если я не укажу таймаут при использовании select.select
. Я бы предпочел не устанавливать тайм-аут. Это просто немного грязно. Тем не менее, все другие способы, с помощью которых я пытался обойти эту проблему, похоже, не работают. Кажется, что Pexpect обошел его, используя os.execv
и pty.fork
вместо subprocess.Popen
и pty.openpty
решение, которое я не предпочитаю. Я делаю что-то неправильно с тем, как я проверяю жизнь подпроцесса? Является ли мой подход неправильным?
Код, который я использую, приведен ниже. Я использую это в Mac OS X 10.6.8, но мне нужно, чтобы он работал и с Ubuntu 12.04.
Это подпроцесс бегун runner.py
:
import subprocess
import select
import pty
import os
import sys
def main():
master, slave = pty.openpty()
process = subprocess.Popen(['python', 'outputter.py'],
stdin=subprocess.PIPE,
stdout=slave, stderr=slave, close_fds=True)
while process.poll() is None:
# Just FYI timeout is the last argument to select.select
rlist, wlist, xlist = select.select([master], [], [])
for f in rlist:
output = os.read(f, 1000) # This is used because it doesn't block
sys.stdout.write(output)
sys.stdout.flush()
print "**ALL COMPLETED**"
if __name__ == '__main__':
main()
Это код подпроцесс outputter.py
. Странные случайные части предназначены для имитации программы, выводящей данные в произвольные интервалы. Вы можете удалить его, если хотите. Это не имеет значения:
import time
import sys
import random
def main():
lines = ['hello', 'there', 'what', 'are', 'you', 'doing']
for line in lines:
sys.stdout.write(line + random.choice(['', '\n']))
sys.stdout.flush()
time.sleep(random.choice([1,2,3,4,5])/20.0)
sys.stdout.write("\ndone\n")
sys.stdout.flush()
if __name__ == '__main__':
main()
Спасибо за любую помощь вы можете предоставить!
Дополнительное примечание
псевдотерминал используется потому, что я хочу, чтобы убедиться, что стандартный вывод не буферизуется.
Спасибо за фантастическое объяснение. Через какое-то время я понял, что, скорее всего, лучше всего установить тайм-аут. Я попробовал решение izhak, но да, после этого я увидел очень странное поведение. Это помогает много! – ravenac95
Для моего собственного улучшения, можете ли вы объяснить, почему мой ответ был коротким? Это должно позволить вам избежать использования тайм-аутов. –
Я внедрил ваши предложения в [ответ на соответствующий вопрос] (http://stackoverflow.com/a/12471855/4279) – jfs