Подтвердить что ты не робот

Понимание Popen.communicate

У меня есть script с именем 1st.py, который создает REPL (read-eval-print-loop):

print "Something to print"
while True:
    r = raw_input()
    if r == 'n':
        print "exiting"
        break
    else:
        print "continuing"

Затем я запустил 1st.py со следующим кодом:

p = subprocess.Popen(["python","1st.py"], stdin=PIPE, stdout=PIPE)

И затем попробовал это:

print p.communicate()[0]

Не удалось выполнить эту трассировку:

Traceback (most recent call last):
  File "1st.py", line 3, in <module>
    r = raw_input()
EOFError: EOF when reading a line

Можете ли вы объяснить, что здесь происходит, пожалуйста? Когда я использую p.stdout.read(), он вешает навсегда.

4b9b3361

Ответ 1

.communicate() запись ввода (в этом случае ввода нет, поэтому он просто закрывает подпроцесс 'stdin, чтобы указать подпроцессу, что больше нет ввода), считывает все выходные данные и ждет выхода из подпроцесса.

Исключение EOFError возникает в дочернем процессе raw_input() (он ожидал данных, но получил EOF (без данных)).

p.stdout.read() зависает вечно, потому что он пытается прочитать весь вывод из дочернего элемента одновременно с тем, как ребенок ждет ввода (raw_input()), который вызывает тупик.

Чтобы избежать тупика, вам нужно читать/писать асинхронно (например, с помощью потоков или выбора) или точно знать, когда и сколько читать/писать, например:

from subprocess import PIPE, Popen

p = Popen(["python", "-u", "1st.py"], stdin=PIPE, stdout=PIPE, bufsize=1)
print p.stdout.readline(), # read the first line
for i in range(10): # repeat several times to show that it works
    print >>p.stdin, i # write input
    p.stdin.flush() # not necessary in this case
    print p.stdout.readline(), # read output

print p.communicate("n\n")[0], # signal the child to exit,
                               # read the rest of the output, 
                               # wait for the child to exit

Примечание: это очень хрупкий код, если чтение/запись не синхронизированы; это взаимоблокировки.

Остерегайтесь проблемы с блочной буферизацией (здесь он решается с помощью "- u" флаг, который отключает буферизацию для stdin, stdout в дочернем элементе).

bufsize=1 делает строки с буферизацией строк на родительской стороне.

Ответ 2

Не используйте связь (input = ""). Он записывает ввод в процесс, закрывает его stdin и затем считывает все выходные данные.

Сделайте это так:

p=subprocess.Popen(["python","1st.py"],stdin=PIPE,stdout=PIPE)

# get output from process "Something to print"
one_line_output = p.stdout.readline()

# write 'a line\n' to the process
p.stdin.write('a line\n')

# get output from process "not time to break"
one_line_output = p.stdout.readline() 

# write "n\n" to that process for if r=='n':
p.stdin.write('n\n') 

# read the last output from the process  "Exiting"
one_line_output = p.stdout.readline()

Что вы сделали бы, чтобы удалить ошибку:

all_the_process_will_tell_you = p.communicate('all you will ever say to this process\nn\n')[0]

Но поскольку связь закрывает stdout и stdin и stderr, вы не можете читать или писать после того, как вы вызвали общение.

Ответ 3

Второй бит кода запускает первый бит кода в качестве подпроцесса с входными и выходными каналами. Затем он закрывает свой вход и пытается прочитать его вывод.

Первый бит кода пытается прочитать со стандартного ввода, но процесс, который запустил его, закрыл его стандартный ввод, поэтому он сразу достигает конца файла, который Python превращается в исключение.