文本环绕时清除终端中的最后一行
Posted
技术标签:
【中文标题】文本环绕时清除终端中的最后一行【英文标题】:Clearing the last line in the terminal when text wraps around 【发布时间】:2017-11-08 00:58:43 【问题描述】:在我的 python 程序中,我想在一个很长的循环中打印进度。我想输出特定信息,如完成百分比等......,但我不希望所有这些输出占据整个屏幕。
理想情况下,我想打印一条进度线。像
train 53/56...x6 │ loss:1.356 │ miou:0.276 │ rate=3.13 Hz, eta=0:00:01, total=0:00:17, wall=19:48 EST
然后,当打印下一行时,我想简单地覆盖这一行。
目前我可以通过在打印消息之前简单地打印回车符'\r'
来做到这一点。这会将光标返回到行首,然后覆盖该行。正是我想要的。
问题是当终端太小而无法容纳整行时,行环绕,回车将我带到环绕行的开头,而不是行的绝对开头。
有没有办法让光标一直回到正确行的开头?
【问题讨论】:
【参考方案1】:您可以使用ANSI escape sequences for cursor movement,最值得注意的是:
定位光标:\033[<L>;<C>H
,或\033[<L>;<C>f
将光标放在 L 行和 C 列。 将光标向上移动 N 行:\033[<N>A
将光标向下移动 N 行:\033[<N>B
将光标向前移动 N 列:\033[<N>C
将光标向后移动 N 列:\033[<N>D
保存光标位置:\033[s
恢复光标位置:\033[u
光标位置保存/恢复似乎很适合您的情况,但不幸的是这两个代码不被许多终端仿真器所接受。
尽管它们在 xterm
和 xfce4-terminal
中工作(除非在终端/滚动输出的最后一行,如 cmets 中的 @ThomasDickey 所述)。试试:
echo -e "\033[s" 1..100 "\033[u" "Overwrite"
对于其他终端仿真器,您可以使用\033[<N>A
试试运气,将光标向上移动到所需的行数,然后移动到0
列。
如果你知道你的行的长度,你可以计算它跨越多少行(如果包装)(bash
示例,注意COLUMNS
环境变量的用法):
line='...'
len=$#line
rows=$((len / COLUMNS))
然后向上移动:
printf "\033[%dA" "$rows"
在 Python 中,您可以像这样使用它:
print("\033[s", "123"*100, "\033[u", "Overwrite", sep='')
print("\033[%dA" % 3, "Overwrite", sep='')
或者,用curses
之类的东西抽象所有这些。
Python 解决方案
基于将光标向上移动 N 行 ANSI 转义序列(应该适用于大多数终端仿真器)和跨 Python 兼容的code for terminal width detection(在 Python3 中您可以使用 shutil.get_terminal_size
),这是一个概念验证演示,它适用于滚动输出、适应行长和不断变化的终端宽度:
#!/usr/bin/env python
from __future__ import print_function
import os
import time
cnt = 0
while True:
with os.popen('stty size', 'r') as stty:
rows, columns = stty.read().split()
line = "Run: , Columns: , Filler: ".format(cnt, columns, "***"*100)
print(line)
print("\033[A".format(len(line) // int(columns) + 1), end='')
time.sleep(1)
cnt += 1
【讨论】:
光标保存/恢复对所询问的场景无济于事,如果输出从屏幕底部开始,因为您要转到的行已移动。 (此外,使用硬编码序列是您在保存/恢复光标时遇到的问题)。 确实如此,在 Python 版本中已修复。 @ThomasDickey,抱歉,我错过了您的评论编辑 - 硬编码序列不起作用是什么意思? 如果您删除 time.sleep(),您可能会遇到竞争条件,导致您得到错误的终端尺寸并导致屏幕充满奇怪的输出。使用诅咒可以解决这个问题吗? 是的,如果您删除sleep
,则在调整大小期间您采样错误终端大小的概率会增加。不要认为诅咒可以帮助你。但是终端调整大小不应该那么频繁,而且OTOH,你为什么需要每毫秒更新一次进度?以上是关于文本环绕时清除终端中的最后一行的主要内容,如果未能解决你的问题,请参考以下文章