清除时防止文本/屏幕闪烁

清除时防止文本/屏幕闪烁

我的脚本做了类似的事情:

while :;
   clear

   do_a_lot_of_output_here

   sleep 1
done

当我执行清除和输出时,是否有任何选项可以防止屏幕闪烁?我想像在watch命令中那样执行此操作(但它写在 中C)。有什么建议吗?

clear | hexdump -C

00000000  1b 5b 48 1b 5b 32 4a                              |.[H.[2J|
00000007

PS. 我bash只使用。

答案1

帮助防止闪烁的一种方法是在清除屏幕之前获取所有输出,以便在清除和重绘屏幕之间留出最短的时间。这与双缓冲的概念类似:

while :; do
   output=$(do_a_lot_of_output_here)
   clear
   echo "$output"
   sleep 1
done

这并不能完全消除闪烁,但根据我的经验,闪烁发生的频率明显较低。

答案2

发生闪烁是因为脚本清除了整个屏幕。如果它覆盖现有文本并仅在必要时清除,则不会出现闪烁。

这是一个例子:

#!/bin/sh
watchit() {
    HOME=$(tput cup 0 0)
    ED=$(tput ed)
    EL=$(tput el)
    ROWS=$(tput lines)
    COLS=$(tput cols)
    printf '%s%s' "$HOME" "$ED"
    while true
    do
        CMD="$@"
        ${SHELL:=sh} -c "$CMD" | head -n $ROWS | while IFS= read LINE; do
            printf '%-*.*s%s\n' $COLS $COLS "$LINE" "$EL"
        done
        printf '%s%s' "$ED" "$HOME"
        sleep 1
    done
}

watchit top -b -n 1

它这样做:

  • 打印给定命令的输出,该输出将适合屏幕(不换行或滚动)
  • 覆盖现有行,清除每行未被覆盖的部分
  • 使用ed终端的功能从当前位置打印到屏幕末尾。

如果您想处理可调整大小的屏幕,您可以将分配移动到外部循环内部,例如ROWSCOLS

#!/bin/sh
watchit() {
    HOME=$(tput cup 0 0)
    ED=$(tput ed)
    EL=$(tput el)
    printf '%s%s' "$HOME" "$ED"
    while true
    do
        ROWS=$(tput lines)
        COLS=$(tput cols)
        CMD="$@"
        ${SHELL:=sh} -c "$CMD" | head -n $ROWS | while IFS= read LINE; do
            printf '%-*.*s%s\n' $COLS $COLS "$LINE" "$EL"
        done
        printf '%s%s' "$ED" "$HOME"
        sleep 1
    done
}

watchit top -b -n 1

因为tput需要从系统获取当前的屏幕尺寸。

进一步阅读:

答案3

闪烁是每次循环清除屏幕时不可避免的结果。您可以将光标移动到屏幕顶部并覆盖旧输出的部分内容。

# You may want to do this if your code is in a script.
unhide_cursor() {
    printf '\e[?25h'
}
trap unhide_cursor EXIT

# Hide the cursor (there is probably a much better way to do this)
printf '\e[?25l'
clear 
while true ; do
    # Move the cursor to the top of the screen but don't clear the screen
    printf '\033[;H' 
    do_a_lot_of_output_here
    sleep 1
done

如果您的输出缩小,此脚本将留下工件。它也不太可能便携。我只用 urxvt、xterm 和 st 对其进行了测试。

答案4

取决于你是一根线还是多根线。 tput 不是一个坏选择,但我喜欢使用 esc 代码......

while true
do
    printf "^[[H^[[0K$do_a_lot_of_output"
    sleep 0.01
done

^[[H将光标移动到 0,0 或左上角,^[[0K 删除从当前光标位置到行尾的内容。

^[[0K很好,因为它将清除从当前光标位置到行尾的行,并且如果下一个字符串恰好比前一个字符串短,则不会看到任何残留数据

一个例子是,如果在第一次迭代期间 $do_a_lot_of_output = 20 ,然后在第二次迭代期间 $do_a_lot_of_output = 5 ,则输出将为 50。如果您不包括 ^[[0K 因为它只会输出它需要的范围, 20 中的 2 被新的 5 覆盖,但 0 仍然会给你一个误导性的 50。

我觉得^[[K也可以用

按 ctrl+v 开始 esc 代码序列,然后按 ESC 生成 ^[,然后添加另一个 [ + 本例中的代码 H 或 K

你把它们放在一起得到 ^[[H 或 ^[[K

Ansi 转义码列表: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797

相关内容