Чтение потокового ввода от subprocess.communicate () - PullRequest
69 голосов
/ 26 апреля 2010

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

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

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

Ответы [ 6 ]

131 голосов
/ 17 июля 2013

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

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

41 голосов
/ 26 апреля 2010

Обратите внимание, я думаю 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,
5 голосов
/ 25 апреля 2013

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

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() может быть предпочтительным, чтобы уменьшить максимальное использование памяти - число, передаваемое ему, является произвольным, но исключая его, можно сразу прочитать весь вывод канала, что, вероятно, нежелательно.

3 голосов
/ 26 апреля 2010

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

2 голосов
/ 22 сентября 2015

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

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'])

См. документы для subprocess.check_call () .

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

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

1 голос
/ 13 ноября 2017
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'))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...