Чтение потокового ввода от 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'))