subprocess.Popen.stdout - чтение stdout в режиме реального времени (снова)
Опять тот же вопрос.
Причина в том, что я все еще не могу заставить его работать после прочтения следующего:
- Перехват stdout в реальном времени из другого процесса в Python
- Перехват stdout подпроцесса во время его работы
- Как получить информацию в режиме реального времени из подпроцесса. Открыть в python (2.5)
- ловить стандартный вывод в реальном времени из подпроцесса
В моем случае у меня есть консольное приложение, написанное на C, давайте возьмем, например, этот код в цикле:
tmp = 0.0;
printf("\ninput>>");
scanf_s("%f",&tmp);
printf ("\ninput was: %f",tmp);
Он постоянно читает некоторые входные данные и записывает некоторые выходные данные.
Мой код на Python для взаимодействия с ним следующий:
p=subprocess.Popen([path],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
p.stdin.write('12345\n')
for line in p.stdout:
print(">>> " + str(line.rstrip()))
p.stdout.flush()
До сих пор, когда я читаю форму p.stdout
он всегда ожидает завершения процесса и затем выводит пустую строку. Я много чего перепробовал - но результат все тот же.
Я пробовал Python 2.6 и 3.1, но версия не имеет значения - мне просто нужно, чтобы она где-то работала.
5 ответов
Попытка записи и чтения из каналов в подпроцесс сложна из-за буферизации по умолчанию, выполняемой в обоих направлениях. Очень легко получить тупик, когда один или другой процесс (родительский или дочерний) читает из пустого буфера, записывает в полный буфер или выполняет блокирующее чтение в буфере, который ожидает данные, прежде чем системные библиотеки очистят его.
Для более скромных объемов данных Popen.communicate()
Метод может быть достаточно. Тем не менее, для данных, которые превышают его буферизацию, вы, вероятно, получите остановленные процессы (аналогично тому, что вы уже видите?)
Возможно, вы захотите узнать подробности об использовании fcntl
модуль и делает один или другой (или оба) из ваших файловых дескрипторов неблокирующими. В этом случае, конечно, вам нужно будет обернуть все операции чтения и / или записи в эти файловые дескрипторы в соответствующую обработку исключений для обработки событий "EWOULDBLOCK". (Я не помню точное исключение Python, которое было поднято для них).
Совершенно другой подход будет для вашего родителя использовать select
модуль и os.fork()
... и для дочернего процесса execve()
целевая программа после непосредственной обработки любого файла dup(). (По сути, вы бы повторно реализовать части Popen()
но с другой обработкой дескриптора родительского файла (PIPE).
Кстати, .communicate, по крайней мере в стандартных библиотеках Python 2.5 и 2.6, будет обрабатывать только около 64 КБ удаленных данных (в Linux и FreeBSD). Это число может варьироваться в зависимости от различных факторов (возможно, включая параметры сборки, используемые для компиляции вашего интерпретатора Python, или версию libc, связанную с ним). Он НЕ просто ограничен доступной памятью (несмотря на утверждение Дж. Ф. Себастьяна об обратном), но ограничен гораздо меньшей величиной.
Вставьте чтение из канала в отдельный поток, который сигнализирует о доступности блока вывода:
Как я могу прочитать все доступные данные из subprocess.Popen.stdout (без блокировки)?
bufsize=256
аргумент мешает 12345\n
от отправки в дочерний процесс в чанке размером менее 256 байт, как это будет при пропуске bufsize
или вставляя p.stdin.flush()
после p.stdin.write()
, Поведение по умолчанию - буферизация строки.
В любом случае вы должны увидеть хотя бы одну пустую строку перед блокировкой, как было показано первым printf(\n...)
в вашем примере.
Ваш конкретный пример не требует взаимодействия в реальном времени. Следующие работы:
from subprocess import Popen, PIPE
p = Popen(["./a.out"], stdin=PIPE, stdout=PIPE)
output = p.communicate(b"12345")[0] # send input/read all output
print output,
где a.out
ваш пример программы на C.
В общем, для диалогового взаимодействия с подпроцессом вы можете использовать pexpect
модуль (или его аналоги в Windows):
import pexpect
child = pexpect.spawn("./a.out")
child.expect("input>>")
child.sendline("12345.67890") # send a number
child.expect(r"\d+\.\d+") # expect the number at the end
print float(child.after) # assert that we can parse it
child.close()
У меня была такая же проблема, и proc.communicate() не решает ее, поскольку ожидает завершения процесса.
Так вот, что работает для меня, на Windows с Python 3.5.1:
import subprocess as sp
myProcess = sp.Popen( cmd, creationflags=sp.CREATE_NEW_PROCESS_GROUP,stdout=sp.PIPE,stderr=sp.STDOUT)
while i<40:
i+=1
time.sleep(.5)
out = myProcess.stdout.readline().decode("utf-8").rstrip()
Я предполагаю, что флаги создания и другие аргументы не являются обязательными (но у меня нет времени на тестирование), так что это будет минимальный синтаксис:
myProcess = sp.Popen( cmd, stdout=sp.PIPE)
for i in range(40)
time.sleep(.5)
out = myProcess.stdout.readline()