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

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

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

subprocess.communicate() видимому, выдает все выходные сразу.

Ответы

Ответ 1

Обратите внимание, я думаю J.F. Метод Себастьяна (ниже) лучше.


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

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

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

Вы можете поймать остальную часть в stdout следующим образом:

output = proc.communicate()[0]
print output,

Ответ 2

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

#!/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 утилиты см Q: Почему бы не просто использовать трубу (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)).

Ответ 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() или read() должна возвращать только пустую строку в EOF после завершения процесса - в противном случае она блокируется, если читать нечего (readline() включает в себя новую строку, поэтому на пустых строках, он возвращает "\n" ). Это позволяет избежать необходимости неуклюжего окончательного вызова communicate() после цикла.

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

Ответ 4

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

Ответ 5

Если вы просто пытаетесь передать результат в реальном времени, вам будет проще сделать это:

import subprocess

# This will raise a CalledProcessError if the program return a nonzero code.
# You can use call() instead if you don't care about that case.
subprocess.check_call(['ls', '-l'])

Смотрите docs для subprocess.check_call().

Если вам нужно обработать вывод, обязательно, на нем. Но если вы этого не сделаете, просто сохраните это.

Изменить: J.F. Себастьян указывает на то, что значения по умолчанию для параметров stdout и stderr передаются в sys.stdout и sys.stderr, и что это произойдет, если sys.stdout и sys.stderr были заменены (скажем, для захвата вывода в тестах).

Ответ 6

myCommand="ls -l"
cmd=myCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r     equally, each as a newline.
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:    
    print(p.stderr.readline().rstrip('\r\n'))