Вопрос-Ответ

Read streaming input from subprocess.communicate()

Чтение потоковых входных данных из subprocess.communicate()

Я использую Python subprocess.communicate() для чтения стандартного вывода из процесса, который выполняется около минуты.

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

subprocess.communicate() кажется, что выдает весь вывод сразу.

Переведено автоматически
Ответ 1

Чтобы получать выходные данные подпроцесса построчно, как только подпроцесс очищает свой буфер стандартного вывода:

#!/usr/bin/env python2
from subprocess import Popen, PIPE

p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1)
with p.stdout:
for line in iter(p.stdout.readline, b''):
print line,
p.wait() # wait for the subprocess to exit

iter() используется для чтения строк сразу после их записи, чтобы обойти ошибку опережающего чтения в Python 2.

Если стандартный вывод подпроцесса использует буферизацию блоков вместо буферизации строк в неинтерактивном режиме (что приводит к задержке вывода до тех пор, пока дочерний буфер не заполнится или не будет явно сброшен дочерним буфером), тогда вы могли бы попытаться принудительно выполнить небуферизованный вывод, используя pexpect, pty модули или unbuffer, stdbuf, script утилиты, см. Вопрос: Почему бы просто не использовать канал (popen())?


Вот код Python 3:

#!/usr/bin/env python3
from subprocess import Popen, PIPE

with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1,
universal_newlines=True) as p:
for line in p.stdout:
print(line, end='')

Примечание: В отличие от Python 2, который выводит байтовые строки подпроцесса как есть; Python 3 использует текстовый режим (вывод cmd декодируется с использованием locale.getpreferredencoding(False) кодировки).

Ответ 2

Пожалуйста, обратите внимание, я думаю, что метод Дж. Ф. Себастьяна (ниже) лучше.


Вот простой пример (без проверки на наличие ошибок):

import subprocess
proc = subprocess.Popen('ls',
shell=True,
stdout=subprocess.PIPE,
)
while proc.poll() is None:
output = proc.stdout.readline()
print output,

Если ls завершается слишком быстро, цикл while может завершиться до того, как вы прочитаете все данные.

Вы можете перехватить остаток в стандартном выводе таким образом:

output = proc.communicate()[0]
print output,
Ответ 3

Я считаю, что самый простой способ собирать выходные данные из процесса потоковым способом выглядит следующим образом:

import sys
from subprocess import *
proc = Popen('ls', shell=True, stdout=PIPE)
while True:
data = proc.stdout.readline() # Alternatively proc.stdout.read(1024)
if len(data) == 0:
break
sys.stdout.write(data) # sys.stdout.buffer.write(data) on Python 3.x

Функция readline() or read() должна возвращать пустую строку только в EOF после завершения процесса - в противном случае она заблокируется, если нечего читать (readline() включает перевод строки, поэтому в пустых строках она возвращает "\n"). Это позволяет избежать необходимости в неудобном финальном communicate() вызове после цикла.

Для файлов с очень длинными строками read() может быть предпочтительнее уменьшить максимальное использование памяти - передаваемое ему число произвольно, но его исключение приводит к считыванию всего вывода канала сразу, что, вероятно, нежелательно.

Ответ 4

Если вам нужен неблокирующий подход, не используйте process.communicate(). Если вы установите для subprocess.Popen() аргумента stdout значение PIPE, вы можете прочитать из process.stdout и проверить, выполняется ли процесс по-прежнему с помощью process.poll().

2023-08-19 16:33 python subprocess