Python, подпроцесс: чтение вывода из подпроцесса - PullRequest
13 голосов
/ 27 сентября 2010

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

#!/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()

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

Ответы [ 6 ]

15 голосов
/ 27 сентября 2010

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

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

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

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

p.stdin.flush()

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

sys.stdout.flush()

import sys вверху)

3 голосов
/ 28 сентября 2010

Если вы хотите передать несколько строк в 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]
3 голосов
/ 27 сентября 2010

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

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

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

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

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

1 голос
/ 27 сентября 2010

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

1 голос
/ 27 сентября 2010

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

-u     : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x
         see man page for details on internal buffering relating to '-u'
0 голосов
/ 27 сентября 2010

Используйте 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 objects в документации для подпроцесса :

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...