Я пытаюсь запустить "docker -compose pull" из скрипта автоматизации Python и постепенно отображать тот же вывод, который выводит команда Docker, если она запускается непосредственно из оболочки. Эта команда печатает строку для каждого изображения Docker, найденного в системе, постепенно обновляет каждую строку с прогрессом загрузки изображения Docker (в процентах) и заменяет этот процент на "выполнено", когда загрузка завершена. Сначала я попытался получить вывод команды с помощью вызовов subprocess.poll () и (blocking) readline ():
import shlex
import subprocess
def run(command, shell=False):
p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
while True:
# print one output line
output_line = p.stdout.readline().decode('utf8')
error_output_line = p.stderr.readline().decode('utf8')
if output_line:
print(output_line.strip())
if error_output_line:
print(error_output_line.strip())
# check if process finished
return_code = p.poll()
if return_code is not None and output_line == '' and error_output_line == '':
break
if return_code > 0:
print("%s failed, error code %d" % (command, return_code))
run("docker-compose pull")
Код застревает в первом (блокирующем) вызове readline (). Затем я попытался сделать то же самое, не блокируя:
import select
import shlex
import subprocess
import sys
import time
def run(command, shell=False):
p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
io_poller = select.poll()
io_poller.register(p.stdout.fileno(), select.POLLIN)
io_poller.register(p.stderr.fileno(), select.POLLIN)
while True:
# poll IO for output
io_events_list = []
while not io_events_list:
time.sleep(1)
io_events_list = io_poller.poll(0)
# print new output
for event in io_events_list:
# must be tested because non-registered events (eg POLLHUP) can also be returned
if event[1] & select.POLLIN:
if event[0] == p.stdout.fileno():
output_str = p.stdout.read(1).decode('utf8')
print(output_str, end="")
if event[0] == p.stderr.fileno():
error_output_str = p.stderr.read(1).decode('utf8')
print(error_output_str, end="")
# check if process finished
# when subprocess finishes, iopoller.poll(0) returns a list with 2 select.POLLHUP events
# (one for stdout, one for stderr) and does not enter in the inner loop
return_code = p.poll()
if return_code is not None:
break
if return_code > 0:
print("%s failed, error code %d" % (command, return_code))
run("docker-compose pull")
Это работает, но только последние строки (с "done" в конце) выводятся на экран, когда все загруженные изображения Docker имеют было завершено.
Оба метода отлично работают с командой с более простым выводом, таким как "ls". Может быть, проблема связана с тем, как эта команда Docker постепенно печатает на экране, перезаписывая уже написанные строки? Существует ли безопасный способ постепенно отображать точный вывод команды в командной строке при ее запуске с помощью сценария Python?
РЕДАКТИРОВАТЬ: 2-й блок кода был исправлен