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

Python, subprocess: чтение вывода из подпроцесса

У меня есть следующий script:

#!/usr/bin/python

while True:
    x = raw_input()
    print x[::-1]

Я вызываю его из ipython:

In [5]: p = Popen('./script.py', stdin=PIPE)

In [6]: p.stdin.write('abc\n')
cba

и он отлично работает.

Однако, когда я это делаю:

In [7]: p = Popen('./script.py', stdin=PIPE, stdout=PIPE)

In [8]: p.stdin.write('abc\n')

In [9]: p.stdout.read()

переводчик висит. Что я делаю не так? Я хотел бы иметь возможность писать и читать из другого процесса несколько раз, чтобы передать некоторые задачи этому процессу. Что мне нужно делать по-другому?

РЕДАКТИРОВАТЬ 1

Если я использую communicate, я получаю следующее:

In [7]: p = Popen('./script.py', stdin=PIPE, stdout=PIPE)

In [8]: p.communicate('abc\n')
Traceback (most recent call last):
  File "./script.py", line 4, in <module>
    x = raw_input()
EOFError: EOF when reading a line
Out[8]: ('cba\n', None)

РЕДАКТИРОВАТЬ 2

Я попытался промыть:

#!/usr/bin/python

import sys

while True:
        x = raw_input()
        print x[::-1]
        sys.stdout.flush()

и здесь:

In [5]: from subprocess import PIPE, Popen

In [6]: p = Popen('./script.py', stdin=PIPE, stdout=PIPE)

In [7]: p.stdin.write('abc')

In [8]: p.stdin.flush()

In [9]: p.stdout.read()

но он снова зависает.

4b9b3361

Ответ 1

Я считаю, что здесь есть две проблемы:

1) Ваш родительский script вызывает p.stdout.read(), который будет считывать все данные до конца файла. Однако ваш дочерний script работает в бесконечном цикле, поэтому конец файла никогда не произойдет. Вероятно, вы хотите p.stdout.readline()?

2) В интерактивном режиме большинство программ блокируют только одну строку за раз. При запуске из другой программы они буферизуют гораздо больше. Буферизация повышает эффективность во многих случаях, но вызывает проблемы, когда две программы должны взаимодействовать в интерактивном режиме.

После p.stdin.write('abc\n') добавить:

p.stdin.flush()

В вашем подпроцессе script после print x[::-1] добавьте в цикл следующее:

sys.stdout.flush()

import sys вверху)

Ответ 2

Для этого может быть полезным метод подпроцесса check_output:

output = subprocess.check_output('./script.py')

И результатом будет вывод stdout из процесса. Если вам также нужен stderr:

output = subprocess.check_output('./script.py', stderr=subprocess.STDOUT)

Поскольку вы избегаете прямого управления каналами, это может обойти вашу проблему.

Ответ 3

Если вы хотите передать несколько строк в script.py, вам необходимо одновременно прочитать/записать:

#!/usr/bin/env python
import sys
from subprocess import PIPE, Popen
from threading  import Thread

def print_output(out, ntrim=80):
    for line in out:
        print len(line)
        if len(line) > ntrim: # truncate long output
            line = line[:ntrim-2]+'..'
        print line.rstrip() 


if __name__=="__main__":
    p = Popen(['python', 'script.py'], stdin=PIPE, stdout=PIPE)
    Thread(target=print_output, args=(p.stdout,)).start()
    for s in ['abc', 'def', 'ab'*10**7, 'ghi']:
        print >>p.stdin, s
    p.stdin.close()
    sys.exit(p.wait()) #NOTE: read http://docs.python.org/library/subprocess.html#subprocess.Popen.wait

Вывод:

4
cba
4
fed
20000001
bababababababababababababababababababababababababababababababababababababababa..
4
ihg

Где script.py:

#!/usr/bin/env python
"""Print reverse lines."""
while True:
    try: x = raw_input()
    except EOFError:
        break # no more input
    else:
        print x[::-1]

или

#!/usr/bin/env python
"""Print reverse lines."""
import sys

for line in sys.stdin:
    print line.rstrip()[::-1]

или

#!/usr/bin/env python
"""Print reverse lines."""
import fileinput

for line in fileinput.input(): # accept files specified as command line arguments
    print line.rstrip()[::-1]

Ответ 4

Вероятно, вы отключите буферизацию вывода Python. Здесь, что python --help должен сказать об этом.

-u     : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x
         see man page for details on internal buffering relating to '-u'

Ответ 5

Когда вы перейдете к p.stdin, закройте его: p.stdin.close()

Ответ 6

Используйте communicate() вместо .stdout.read().

Пример:

from subprocess import Popen, PIPE
p = Popen('./script.py', stdin=PIPE, stdout=PIPE, stderr=PIPE)
input = 'abc\n'
stdout, stderr = p.communicate(input)

Эта рекомендация приведена в разделе Объекты Popen в подпроцессе документации:

Предупреждение: используйте функцию связи(), а не .stdin.write,.stdout.read или .stderr.read чтобы избежать взаимоблокировок из-за того, что любой из других буферов труб ОС заполняет и блокирует дочерний процесс.