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

Ero*_*mic 4 python terminal stdout ansi

在我的 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
Run Code Online (Sandbox Code Playgroud)

然后,当下一行打印时,我想简单地覆盖这一行。

目前,我可以通过'\r'在打印消息之前简单地打印回车来完成此操作。这会将光标返回到行的开头,然后覆盖该行。正是我想要的。

问题是当终端太小而无法容纳整行时,该行环绕并且回车将我带到环绕行的开头,而不是该行的绝对开头。

有没有办法可以让光标一直回到正确行的开头?

ran*_*mir 5

您可以使用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

光标位置保存/恢复似乎是您的理想选择,但不幸的是,许多终端仿真器不支持这两个代码。

他们在工作xtermxfce4-terminal虽然(除终端/滚动的输出的最后一行时,由@ThomasDickey在评论中所指出)。尝试:

echo -e "\033[s" {1..100} "\033[u" "Overwrite"
Run Code Online (Sandbox Code Playgroud)

对于其他终端模拟器,您可以试试运气,\033[<N>A将光标向上移动所需的行数,然后移动到 column 0

如果你知道你的行的长度,你可以计算它在(以及如果被包裹)时跨越多少行(bash例如,注意COLUMNS环境变量的用法):

line='...'
len=${#line}
rows=$((len / COLUMNS))
Run Code Online (Sandbox Code Playgroud)

然后向上移动:

printf "\033[%dA" "$rows"
Run Code Online (Sandbox Code Playgroud)

在 Python 中,你可以像这样使用它:

print("\033[s", "123"*100, "\033[u", "Overwrite", sep='')
print("\033[%dA" % 3, "Overwrite", sep='')
Run Code Online (Sandbox Code Playgroud)

或者,用类似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
Run Code Online (Sandbox Code Playgroud)