当文本换行时,清除终端中的最后一行。

4
在我的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个回答

6
您可以使用 ANSI转义序列进行光标移动,其中最重要的是:
  • 定位光标:\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
光标位置的保存/恢复似乎非常适合您的情况,但不幸的是,这两个代码并未被许多终端模拟器支持。

尽管在终端的最后一行/滚动输出时会出现问题(如@ThomasDickey在评论中指出),但它们可以在xtermxfce4-terminal中运行。请尝试:

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 兼容的 终端宽度检测代码(在 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

如果输出从屏幕底部开始,那么光标保存/恢复不会解决要求的场景,因为您想要前往的行已经移动了。(此外,使用硬编码序列是您在光标保存/恢复方面遇到的问题)。 - Thomas Dickey
那是正确的,在Python版本中已经修复了。 - randomir
@ThomasDickey,抱歉,我错过了您的评论编辑 - 您所说的硬编码序列无法工作是什么意思? - randomir
如果您删除time.sleep(),似乎会遇到竞争条件,导致您获取错误的终端大小并导致屏幕填充奇怪的输出。使用curses是否可以解决这个问题? - Erotemic
是的,如果你移除 sleep,在 resize 过程中你采样到错误的终端尺寸的概率会增加。我认为 curses 也无法解决这个问题。但是终端 resize 并不应该那么频繁发生,另一方面,你为什么需要每毫秒更新一次进度呢? - randomir
xterm支持两种不同的序列来保存/恢复光标位置,大多数模仿xterm的终端仅支持其中一种。终端描述将列出可用的一种... - Thomas Dickey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接