Использование подпроцесса с select и pty зависает при захвате вывода
Я пытаюсь написать программу на Python, способную взаимодействовать с другими программами. Это означает отправку стандартного ввода и получение данных стандартного вывода. Я не могу использовать pexpect (хотя это определенно вдохновило некоторые дизайны). Процесс, который я сейчас использую, заключается в следующем:
Attach a pty to the subprocess's stdout Loop until the subprocess exits by checkingsubprocess.poll
When there is data available in the stdout write that data immediately to the current stdout.
Finish!
Я прототипировал некоторый код (ниже), который работает, но, похоже, у меня есть один недостаток, который меня беспокоит. После завершения дочернего процесса родительский процесс зависает, если я не указываю время ожидания при использовании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
. The strange random parts are just to simulate a program outputting data at random intervals. You can remove it if you wish. It shouldn't matter:
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()
Спасибо за любую помощь, которую вы все можете предоставить!
Extra note
pty используется, потому что я хочу убедиться, что stdout не буферизован.