文本环绕时清除终端中的最后一行

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

光标位置保存/恢复似乎很适合您的情况,但不幸的是这两个代码不被许多终端仿真器所接受。

尽管它们在 xtermxfce4-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,你为什么需要每毫秒更新一次进度?

以上是关于文本环绕时清除终端中的最后一行的主要内容,如果未能解决你的问题,请参考以下文章

VScode:编辑时执行最后一个终端命令的热键?

如何使用 bash 删除和替换终端中的最后一行?

shell 终端terminfo命令 tput

Windows终端(用的是powershell)中的vim输入全部是乱码

在 Nodemon 运行时清除终端?

粘贴到 SSH 终端大写最后一个字符,不允许我编辑它