Чтение/запись в подпроцесс Popen()

Я пытаюсь поговорить с дочерним процессом, используя вызов python subprocess.Popen(). В моем реальном коде я реализую тип IPC, поэтому я хочу записать некоторые данные, прочитать ответ, записать еще несколько данных, прочитать ответ и так далее. Из-за этого я не могу использовать Popen.communicate(), который хорошо работает в простом случае.

Этот код показывает мою проблему. Он даже не получает первого ответа, зависает при первом «Результате чтения». Почему? Как я могу заставить это работать, как я ожидаю?

import subprocess
p = subprocess.Popen(["sed", 's/a/x/g'],
                     stdout = subprocess.PIPE,
                     stdin = subprocess.PIPE)

p.stdin.write("abc\n")
print "Reading result:"
print p.stdout.readline()

p.stdin.write("cat\n")
print "Reading result:"
print p.stdout.readline()

person Mats Ekberg    schedule 28.04.2012    source источник


Ответы (2)


Я бы попытался использовать Popen().communicate(), если вы можете, так как он делает для вас много хороших вещей, но если вам нужно использовать Popen() точно так, как вы описали, вам нужно настроить sed для очистки буфера после новой строки с параметром -l:

p = subprocess.Popen(['sed', '-l', 's/a/x/g'],
                     stdout=subprocess.PIPE,
                     stdin=subprocess.PIPE)

и ваш код должен работать нормально

person gnr    schedule 28.04.2012
comment
Действительно работает! Мой sed почему-то использует -u для небуферизации, а не -l, но все равно работает. Это решает мой код примера, но, к сожалению, не мой реальный код, так как фактическая команда не sed, а другая программа python. Хороший ответ, однако, вы точно определили проблему. - person Mats Ekberg; 28.04.2012
comment
Ага, проблема решена. Проблема заключалась в буферизации вывода результата. Выполнение простого stdout.flush() в моем подпроцессе решило проблему. Спасибо! - person Mats Ekberg; 28.04.2012

Вывод sed буферизуется и выводит свои данные только до тех пор, пока не будет накоплено достаточно или входной поток не будет исчерпан и закрыт.

Попробуй это:

import subprocess
p = subprocess.Popen(["sed", 's/a/x/g'],
                     stdout = subprocess.PIPE,
                     stdin = subprocess.PIPE)

p.stdin.write("abc\n")
p.stdin.write("cat\n")
p.stdin.close()

print "Reading result 1:"
print p.stdout.readline()

print "Reading result 2:"
print p.stdout.readline()

Имейте в виду, что это невозможно сделать надежно, поскольку огромные данные, записываемые в stdin, блокируются после заполнения буфера. Лучше всего использовать communicate().

person glglgl    schedule 28.04.2012
comment
Но sed не действует таким образом при непосредственном запуске команды в оболочке. Если вы это сделаете, ответ придет после каждой строки. И проблема остается, даже если я вызываю p.stdin.flush() после написания. Кроме того, в реальной жизни я также написал вызываемую программу, буферизации нет и она ведет себя так же. Я не уверен, что здесь проблема с буферизацией. - person Mats Ekberg; 28.04.2012
comment
add bufsize=0 может решить вашу проблему. subprocess.Popen(['sed', '-l', 's/a/x/g'], bufsize=0, stdin=PIPE, stdout=PIPE) - person codeskyblue; 27.11.2019