【发布时间】:2020-03-28 21:42:25
【问题描述】:
我正在尝试从 Python 自动化脚本中运行“docker-compose pull”,并以增量方式显示 Docker 命令在直接从 shell 中运行时会打印的相同输出。此命令为系统中找到的每个 Docker 映像打印一行,使用 Docker 映像的下载进度(百分比)增量更新每一行,并在下载完成时将此百分比替换为“完成”。我首先尝试使用 subprocess.poll() 和(阻塞)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")
这可行,但只有最后几行(末尾带有“完成”)会在所有 Docker 映像下载完成后打印到屏幕上。
这两种方法都适用于具有更简单输出的命令,例如“ls”。也许问题与这个 Docker 命令如何逐渐打印到屏幕上,覆盖已经写好的行有关?当通过 Python 脚本运行命令时,是否有一种安全的方法可以在命令行中逐步显示命令的确切输出?
编辑:第二个代码块已更正
【问题讨论】:
标签: python subprocess output