如何设置进度条并打印 subprocess.Popen 的输出
How to set up progress bar and also print output from subprocess.Popen
我正在尝试使用进度条来减少使用 python subprocess
时 运行 FPGA 构建脚本的冗长程度。构建脚本可能需要数小时 运行,并且构建输出非常冗长。目前,我为用户打印脚本的输出,以查看正在取得的进展并了解进展情况。我想为用户清理这个,因为冗长掩盖了重要的 python 日志,这真的很烦人。
本质上,我希望用tqdm
来表示进度。我想要的是这个(它不必完全匹配下面的示例):
- 第一行显示进度指示器,即使进度没有增加,经过的时间仍应更新。理想情况下,这将是一个像纺车一样的进度指示器,但我可以制作一个带有步骤的进度条
- 第二行将构建输出显示为实质上的进度通知。这应该与构建的输出同步。我希望它在
subprocess.Popen
完成后消失,但这不是必需的
构建开始时,终端可能如下所示:
INFO: starting build
Running Build [ ] (0:00:01.2345)
@I: compiling file <x>
完成后,它看起来像这样(注意终端输出线不见了)
INFO: starting build
Running Build [...........................] (6:25:48.6251)
<python continues logging stuff here>
为了确定进度,我将检查 subprocess.Popen.stdout
以确定它是否是构建脚本中的一行。这是一些伪代码来展示我正在尝试做的事情:
from subprocess import Popen, PIPE
pb = <initialize progress bar>
lines_in_build_script = [
"step 1",
"step 2",
]
p = Popen(cmd, shell=True, stdout=PIPE)
for line in p.stdout:
pb.update_build_output(line)
if line in lines_in_build_script:
pb.increment_progress() # I would prefer if it was just a spinning wheel because looking for line in output is asking for trouble
rc = p.wait()
编辑:tqdm
不是必需的,这只是我知道的一个包。我愿意接受有关如何解决此问题的任何建议。
我对 tqdm
或 alive_progress
功能不满意。我发现 yaspin
并创建了一个自定义的“标题”格式化程序,因为根据我用来确定进度的步骤,设置包的百分比是不可能表征的。相反,我向 yaspin 格式化程序添加了一个计数器以及一些手动更新文本的方法。不完全是我想要的,但它绝对简化了标准输出:
class BarFormat:
def __init__(self, title: str, total=None, width=None):
self.title = title
self._total = total
self._cnt = 0
self._start = datetime.datetime.now()
self._status = ""
self._width = width if width else os.get_terminal_size(0)[0]
def __str__(self):
delta = datetime.datetime.now() - self._start
if self._total:
text = f"{self.title} ({self._cnt}/{self._total} elapsed {delta}) {self._status}"
else:
text = f"{self.title} (elapsed {delta}) {self._status}"
# resize if needed
return text[:self._width-10]
def current(self) -> int:
return self._cnt
def incr(self) -> None:
self._cnt += 1
def update(self, status: str) -> None:
self._status = status.strip()
然后在我的代码中,我有这样的东西:
fmt = BarFormat(bar_title, total=len(bar_items))
with yaspin(text=fmt).green.arc as bar:
for line in p.stdout:
l = line.decode('utf-8').strip()
if l:
# update status of bar only if there's output
fmt.update(l)
if bar_items:
if l == bar_items[fmt.current()]:
bar.write("> "+l)
fmt.incr()
rc = p.wait()
# final spinner status
if rc in ignore_rcs:
bar.green.ok()
else:
bar.red.fail()
这会产生如下内容:
...
> compile code
> <other steps...>
<spinner> Running Some Tool (125/640 elapsed 0:12:34.56789) @I: some info
我注意到 yaspin
不处理文本字段中超出终端宽度的行。我这样做是为了在实例化 class 时确定宽度。不过,将其更新为动态的会非常容易。
编辑:我实际上放弃了 yaspin
。我开始使用 rich
而不是因为我决定更新我的一些日志以使用 RichHandler
并发现它支持微调器和进度条。这是有关 https://rich.readthedocs.io/en/latest/progress.html
的信息
这是一个工作示例,原始问题使用 enlighten. Enlighten will keep the progress bar at the bottom of the terminal and you can print whatever you like through normal methods. See the docs 如何自定义。
from subprocess import Popen, PIPE
import enlighten
cmd = 'for i in {1..80}; do sleep 0.2; echo $i; [ $((i % 10)) -eq 0 ] && echo step $((i/10)); done'
lines_in_build_script = [
'step 1\n',
'step 2\n',
'step 3\n',
'step 4\n',
'step 5\n',
'step 6\n',
'step 7\n',
'step 8\n',
]
manager = enlighten.get_manager()
pb = manager.counter(total=len(lines_in_build_script), desc='Long Process')
p = Popen(cmd, shell=True, stdout=PIPE, text=True)
pb.refresh()
for line in p.stdout:
print(line, end='')
if line in lines_in_build_script:
pb.update()
rc = p.wait()
alive-progress 的作者在这里!
实际上,alive-progress
确实支持将文本放在上面,将它们放在下面没有多大意义,因为终端上的行 运行 从上到下,所以栏必须留下所有像日志一样打印的行。
今天发布了最终收据的隐藏,连同已经支持的旋转器隐藏,你可以这样操作:
是的,所有这些都有很棒的现场旋转器!
我认为显示经过的时间非常酷,但如果您愿意,也可以删除它,以及“请稍候”消息,如下所示:
我正在尝试使用进度条来减少使用 python subprocess
时 运行 FPGA 构建脚本的冗长程度。构建脚本可能需要数小时 运行,并且构建输出非常冗长。目前,我为用户打印脚本的输出,以查看正在取得的进展并了解进展情况。我想为用户清理这个,因为冗长掩盖了重要的 python 日志,这真的很烦人。
本质上,我希望用tqdm
来表示进度。我想要的是这个(它不必完全匹配下面的示例):
- 第一行显示进度指示器,即使进度没有增加,经过的时间仍应更新。理想情况下,这将是一个像纺车一样的进度指示器,但我可以制作一个带有步骤的进度条
- 第二行将构建输出显示为实质上的进度通知。这应该与构建的输出同步。我希望它在
subprocess.Popen
完成后消失,但这不是必需的
构建开始时,终端可能如下所示:
INFO: starting build
Running Build [ ] (0:00:01.2345)
@I: compiling file <x>
完成后,它看起来像这样(注意终端输出线不见了)
INFO: starting build
Running Build [...........................] (6:25:48.6251)
<python continues logging stuff here>
为了确定进度,我将检查 subprocess.Popen.stdout
以确定它是否是构建脚本中的一行。这是一些伪代码来展示我正在尝试做的事情:
from subprocess import Popen, PIPE
pb = <initialize progress bar>
lines_in_build_script = [
"step 1",
"step 2",
]
p = Popen(cmd, shell=True, stdout=PIPE)
for line in p.stdout:
pb.update_build_output(line)
if line in lines_in_build_script:
pb.increment_progress() # I would prefer if it was just a spinning wheel because looking for line in output is asking for trouble
rc = p.wait()
编辑:tqdm
不是必需的,这只是我知道的一个包。我愿意接受有关如何解决此问题的任何建议。
我对 tqdm
或 alive_progress
功能不满意。我发现 yaspin
并创建了一个自定义的“标题”格式化程序,因为根据我用来确定进度的步骤,设置包的百分比是不可能表征的。相反,我向 yaspin 格式化程序添加了一个计数器以及一些手动更新文本的方法。不完全是我想要的,但它绝对简化了标准输出:
class BarFormat:
def __init__(self, title: str, total=None, width=None):
self.title = title
self._total = total
self._cnt = 0
self._start = datetime.datetime.now()
self._status = ""
self._width = width if width else os.get_terminal_size(0)[0]
def __str__(self):
delta = datetime.datetime.now() - self._start
if self._total:
text = f"{self.title} ({self._cnt}/{self._total} elapsed {delta}) {self._status}"
else:
text = f"{self.title} (elapsed {delta}) {self._status}"
# resize if needed
return text[:self._width-10]
def current(self) -> int:
return self._cnt
def incr(self) -> None:
self._cnt += 1
def update(self, status: str) -> None:
self._status = status.strip()
然后在我的代码中,我有这样的东西:
fmt = BarFormat(bar_title, total=len(bar_items))
with yaspin(text=fmt).green.arc as bar:
for line in p.stdout:
l = line.decode('utf-8').strip()
if l:
# update status of bar only if there's output
fmt.update(l)
if bar_items:
if l == bar_items[fmt.current()]:
bar.write("> "+l)
fmt.incr()
rc = p.wait()
# final spinner status
if rc in ignore_rcs:
bar.green.ok()
else:
bar.red.fail()
这会产生如下内容:
...
> compile code
> <other steps...>
<spinner> Running Some Tool (125/640 elapsed 0:12:34.56789) @I: some info
我注意到 yaspin
不处理文本字段中超出终端宽度的行。我这样做是为了在实例化 class 时确定宽度。不过,将其更新为动态的会非常容易。
编辑:我实际上放弃了 yaspin
。我开始使用 rich
而不是因为我决定更新我的一些日志以使用 RichHandler
并发现它支持微调器和进度条。这是有关 https://rich.readthedocs.io/en/latest/progress.html
这是一个工作示例,原始问题使用 enlighten. Enlighten will keep the progress bar at the bottom of the terminal and you can print whatever you like through normal methods. See the docs 如何自定义。
from subprocess import Popen, PIPE
import enlighten
cmd = 'for i in {1..80}; do sleep 0.2; echo $i; [ $((i % 10)) -eq 0 ] && echo step $((i/10)); done'
lines_in_build_script = [
'step 1\n',
'step 2\n',
'step 3\n',
'step 4\n',
'step 5\n',
'step 6\n',
'step 7\n',
'step 8\n',
]
manager = enlighten.get_manager()
pb = manager.counter(total=len(lines_in_build_script), desc='Long Process')
p = Popen(cmd, shell=True, stdout=PIPE, text=True)
pb.refresh()
for line in p.stdout:
print(line, end='')
if line in lines_in_build_script:
pb.update()
rc = p.wait()
alive-progress 的作者在这里!
实际上,alive-progress
确实支持将文本放在上面,将它们放在下面没有多大意义,因为终端上的行 运行 从上到下,所以栏必须留下所有像日志一样打印的行。
今天发布了最终收据的隐藏,连同已经支持的旋转器隐藏,你可以这样操作:
是的,所有这些都有很棒的现场旋转器!
我认为显示经过的时间非常酷,但如果您愿意,也可以删除它,以及“请稍候”消息,如下所示: