Python: чтение потокового ввода из подпроцесса.взаимодействовать()



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



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



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

398   6  

6 ответов:

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


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

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,

чтобы получить вывод подпроцесса строка за строкой, как только подпроцесс сбрасывает свой буфер 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.

если stdout подпроцесса использует буферизацию блоков вместо буферизации строк в неинтерактивном режиме (что приводит к задержке вывода до тех пор, пока буфер дочернего элемента не будет заполнен или явно сброшен дочерним элементом), то вы можете попробовать принудительный выход без буферизации с помощью 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, который выводит подпроцесс ' bytestrings как есть; Python 3 использует текстовый режим (выход cmd декодируется с помощью locale.getpreferredencoding(False) кодировка).

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

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

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

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

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

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

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

посмотреть документы для подпроцесса.check_call ().

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

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

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

Comments

    Ничего не найдено.