Python: чтение потокового ввода из подпроцесса.взаимодействовать()
Я использую в Python subprocess.communicate() для чтения stdout из процесса, который выполняется около минуты.
Как я могу распечатать каждую строку stdout этого процесса в потоковом режиме, чтобы я мог видеть вывод, как он генерируется, но все еще блокировать процесс, завершающийся перед продолжением?
subprocess.communicate() кажется, чтобы дать все выходные данные сразу.
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.xThe
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