Я использую Python subprocess.communicate()
для чтения stdout из процесса, который выполняется около минуты.
Как я могу распечатать каждую строку этого процесса stdout
в потоковом режиме, чтобы я мог видеть результат в том виде, в каком он сгенерирован, но по-прежнему блокировать завершение процесса перед продолжением?
subprocess.communicate()
кажется, дает сразу весь результат.
python
subprocess
Генрих Шметтерлинг
источник
источник
Ответы:
Обратите внимание, я думаю, что метод Дж. Ф. Себастьяна (ниже) лучше.
Вот простой пример (без проверки на ошибки):
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,
источник
while proc.poll() is None: time.sleep(0)
или чем-то в этом роде. По сути, вам нужно либо убедиться, что вывод новой строки - это последнее, что делает процесс (потому что вы не можете дать интерпретатору время для повторного цикла), либо вам нужно сделать что-то «необычное».Чтобы получить вывод подпроцесса построчно, как только подпроцесс очищает свой буфер 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, который выводит байтовые строки подпроцесса как есть; Python 3 использует текстовый режим (вывод cmd декодируется с использованием
locale.getpreferredencoding(False)
кодировки).источник
b''
являетсяbytes
литералом в Python 2.7 и Python 3.bufsize=1
может иметь значение, если вы также пишете (используетеp.stdin
) в подпроцесс, например, это может помочь избежать тупика при выполнении интерактивного (pexpect
-подобного) обмена - при условии, что в самом дочернем процессе нет проблем с буферизацией. Если вы только читаете, то, как я уже сказал, разница только в производительности: если это не так, не могли бы вы предоставить минимальный полный пример кода, который это показывает?stderr=subprocess.STDOUT
кPopen()
). См. Также ссылки на решения для потоковой передачи или asyncio .stdout=PIPE
не захватывает вывод (вы все еще видите его на экране), тогда ваша программа может вместо этого печатать на stderr или напрямую на терминал. Чтобы объединить stdout и stderr, пройдитеstderr=subprocess.STDOUT
(см. Мой предыдущий комментарий). Для захвата вывода, распечатанного непосредственно на вашем tty, вы можете использовать решения pexpect, pty. . Вот более сложный пример кода .Я считаю, что самый простой способ собрать вывод процесса в потоковом режиме выглядит так:
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()
orread()
должна возвращать только пустую строку в EOF после завершения процесса - в противном случае она будет блокироваться, если нечего читать (readline()
включает новую строку, поэтому в пустых строках она возвращает "\ n"). Это позволяет избежать неудобного финальногоcommunicate()
вызова после цикла.Для файлов с очень длинными строками
read()
может быть предпочтительнее уменьшить максимальное использование памяти - число, передаваемое в него, является произвольным, но его исключение приводит к одновременному чтению всего вывода канала, что, вероятно, нежелательно.источник
data = proc.stdout.read()
блоки, пока не будут прочитаны все данные. Вы можете спутать это с тем,os.read(fd, maxsize)
что можно вернуть раньше (как только будут доступны какие-либо данные).read()
он работает нормально, а такжеreadline()
отлично работает до тех пор, пока максимальная длина строки является разумной. Соответственно обновил свой ответ.Если вам нужен неблокирующий подход, не используйте
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'])
См. Документацию для subprocess.check_call () .
Если вам нужно обработать вывод, конечно, зацикливайтесь на нем. Но если вы этого не сделаете, просто сделайте это просто.
Изменить: JF Себастьян указывает на то, что значения по умолчанию для параметров stdout и stderr передаются в sys.stdout и sys.stderr, и что это не удастся, если sys.stdout и sys.stderr были заменены (например, для захвата вывода в тесты).
источник
sys.stdout
илиsys.stderr
заменяются файл-подобными объектами , которые не имеют никакого реального fileno (). Еслиsys.stdout
,sys.stderr
не будут заменены , то это еще проще:subprocess.check_call(args)
.call()
более ,check_call()
если я не хочу, чтобыCalledProcessError
.python -mthis
: "Ошибки никогда не должны передаваться без уведомления. Если не отключены явно." именно поэтому пример кода следует предпочестьcheck_call()
болееcall()
.call()
возвращают ненулевые коды ошибок в условиях отсутствия ошибок, потому что они ужасны. В нашем случае ненулевой код ошибки на самом деле не является ошибкой.grep
которые могут возвращать ненулевой статус выхода, даже если нет ошибки - это исключения. По умолчанию нулевой статус выхода указывает на успех.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'))
источник
shlex.split(myCommand)
вместоmyCommand.split()
. Он также учитывает пробелы в цитируемых аргументах.Добавление еще одного решения python3 с небольшими изменениями:
with
конструкции)import subprocess import sys def subcall_stream(cmd, fail_on_error=True): # Run a shell command, streaming output to STDOUT in real time # Expects a list style command, e.g. `["docker", "pull", "ubuntu"]` p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) for line in p.stdout: sys.stdout.write(line) p.wait() exit_code = p.returncode if exit_code != 0 and fail_on_error: raise RuntimeError(f"Shell command failed with exit code {exit_code}. Command: `{cmd}`") return(exit_code)
источник