progressbar 颜色_Linux技巧:在代码中设置终端字符显示颜色和移动光标位置

本篇文章介绍如何在代码中使用 ANSI 转义码来设置终端的字符显示颜色、移动光标位置等,并实现一个进度条百分比跳变的效果。

ANSI 转义码

在 Linux 中,可以使用 ANSI 转义码(ANSI escape codes)设置终端的字符显示颜色、移动光标位置、清除字符显示等。

ANSI 转义码是由终端自身支持,独立于编程语言之外,可以在 C 语言、Java、Python、或者 Shell 中使用。

下面以 bash shell 为例来说明如何使用 ANSI 转义码。

ANSI 转义码格式

ANSI 转义码由一串 ASCII 编码的字符串组成,要求以 ASCII 编码的 Escape 字符和 [ 字符开头,后面跟着具体的转义码,指定相应的操作。基本格式如下:

Esc[escape code

Escape 字符也就是 Esc 键对应的字符。

由于按 Esc 键,不会得到一个可显示的字符,需要用具体的编码值来表示这个字符。

在不同编程语言中,表示字符编码值的写法可能不一样。一般常用 e 转义字符来表示 Esc 字符。

使用 echo 命令测试 ANSI 转义码

在 bash shell 中,可以使用 echo 命令的 -e 选项来测试 ANSI 转义码。

查看 man echo 对 -e 选项说明如下:

-e

enable interpretation of backslash escapes.

If -e is in effect, the following sequences are recognized:

e

escape

0NNN

byte with octal value NNN (1 to 3 digits).

即,在 echo 命令中,-e 选项可以指定处理转义字符。

e 转义字符表示 escape 字符。

0NNN 转义字符使用八进制来获取 NNN 编码值对应的字符。

在 ASCII 编码中,Escape 字符对应的八进制值是 033。

则在 echo 命令中,033 表示 escape 字符。

使用 echo 命令测试 ANSI 转义码时,可以写为 echo -e "033[31m"。

这里的 31m 转义码表示要把终端字符的前景色设成红色。

Linux 的 printf 命令也可以输出 ANSI 转义码,而且不需要加 -e 选项,例如写为 `printf "e[31m"。

注意:这里需要用双引号、或者单引号把 033[31m 括起来,避免 bash 自身对 进行转义,会去掉 字符,导致 echo 命令收不到 字符,无法处理转义字符。

也可以写为 echo -e "e[31m",e 也表示 escape 字符。

后面测试的时候,统一使用 e 的形式,少输入一些字符。

具体测试如下:

71b932f261b1f5fbc1e31a82e51c5133.png

使用ANSI转义码设置终端的字符显示颜色

执行 echo -e "e[31m" 命令后,终端的提示字符会变成红色,之后输入的字符也都会变成红色。

即,终端的默认字符颜色变成了红色。

执行 echo -e "e[0m" 命令重置终端属性,让终端的字符颜色变成原来的默认颜色。

这里的 0m 转义码表示重置字符显示属性。

一般来说,为了不影响终端自身的显示,使用 ANSI 转义码设置某个字符串的显示颜色后,建议随后使用 0m 转义码来重置为原来的颜色。

举例说明如下:

62cda5d59acd4194e8b54eb1b4b1bee0.png

只修改指定字符串的显示颜色

在上面命令中,e[31m 是一个 ANSI 转义码,表示设置终端字符颜色为红色。

e[0m 也是一个 ANSI 转义码,表示重置终端的颜色属性,会恢复成原来的颜色。

在这两个转义码中间的字符串会显示在终端上。

执行该命令后,终端的提示符会显示为原来的颜色。

设置终端字符颜色的 ANSI 转义码

下面详细说明设置终端字符颜色的 ANSI 转义码,其基本格式如下:

Esc[Value;...;Valuem

这里的 Value 可以提供多个值,不同值之间用分号 ‘;’ 隔开。

这些值可以分别指定字符的前景色、背景色、字符属性(粗体、下划线、反转)。它们之间的顺序不限。

转义码最后以 m 字符结尾。

设置字符前景色的值如下:

3c474994b67eedc99b9568e269de310c.png

ANSI转义码设置字符前景色

设置字符背景色的值如下:

74d422f3d8645b223b85a304cdfb0c75.png

ANSI转义码设置字符背景色

设置字符属性的值如下:

58b619657432cb85a84d47b6c6230ae1.png

ANSI转义码设置字符显示属性

具体举例如下:

3797ee2446a2f61f876069376062a918.png

设置字符串显示的前景色和背景色

可以看到,e[31;44m、e[44;31m 这两个转义码设置的字符颜色效果是一样的。所给的前景色、背景色没有要求先后顺序。

目前的大部分终端都支持 256 色,可以使用 Esc[38;5;Valuem 来设置终端字符为 256 色。这里的 Value 取值是 0-255。

例如,echo -e "e[38;5;111mAAAAAAe[0m" 命令设置为 111 对应的颜色。

具体的颜色取值可以查看 256 色的颜色表。网上的很多文章都有说明。这里不再列举。

使用 ANSI 转义码移动终端光标

ANSI 转义码可以用来移动终端的光标位置,从而改变字符的输出位置。

具体举例如下:

$ echo -e "123456789e[4Dabc"12345abc9

在这个命令中,e[4D 转义码表示把光标往左移动 4 列。

可以看到,光标移动 4 列后,位于字符 6 所在的位置,重新输出 abc,覆盖了原来的 678 三个字符。

移动光标的具体转义码说明如下:

22f53ca10ed12ed9c74b474dd01dc9c7.png

移动终端光标位置的ANSI转义码

上面所说的终端位置指的是终端可见的窗口位置,不包括缓冲区位置。

即,窗口显示不会发生滚动,只在当前可见的窗口区域跳转光标。

注意:由于 echo 命令默认会输出换行符,导致移动光标后再次换行,会对光标移动效果造成干扰。

在测试移动光标的转义码时,建议用 printf 命令测试。该命令默认不会输出换行符。

由于 bash 里面需要按下回车才执行命令,会影响光标的左右移动效果,建议在 printf 自身输出的内容中左右移动光标。

实际测试发现,光标右移 n 列,光标会位于第 n 列的后面,之后输出的字符串会从 n+1 列开始。

Esc[C、Esc[0C、和 Esc[1C 的效果相同,都是光标右移 1 列。

类似的,Esc[D、Esc[0D、和 Esc[1D 的效果相同,都是光标左移 1 列。

使用 printf 命令测试如下:

$ printf "123456789e[1Da"12345678a$ printf "123456789e[0Da"12345678a$ printf "123456789e[Da"12345678a

可以看到,使用 e[D、e[0D、e[1D 往左移动光标,然后输出字符 a,都是覆盖同一个字符 9。

这三个转义码的光标移动效果相同。

$ printf "123456789e[4Da"12345a789$ printf "123456789e[4De[Ca"123456a89$ printf "123456789e[4De[0Ca"123456a89$ printf "123456789e[4De[1Ca"123456a89

e[4D 把光标左移 4 列,移动到字符 6 的位置。

e[C、e[0C、e[1C 都是往右移动光标到下一列,到字符 7 的位置,输出字符 a,覆盖了字符 7。

通过移动光标实现进度百分比的效果

我们可以通过移动光标实现进度百分比的效果。假设有一个 progress.sh 脚本,内容如下:

#!/bin/bashfor ((i = 0; i <= 100; ++i)); do    printf "e[5D%3d%%" $i    sleep 0.1sdoneecho

这里使用 printf 命令进行输出,以便格式化字符串。printf 命令也是使用 e 来表示 escape 字符。

e[5D 转义码表示把光标左移 5 列。

由于所输出的字符不超过 5 个字符,每次光标左移 5 列,都会移动到最左边,从第一列开始输出。

那么后面输出的内容会覆盖前面输出的内容,达到在同一行重复输出的效果。

sleep 0.1s 命令表示暂停 0.1 秒。添加这个语句,以便清楚地看到进度百分比跳变。否则执行过快,百分比很快就跳到 100%。

执行 progress.sh 脚本的结果如下:

$ ./progress.sh100%

这里不是动图,看不到进度百分比跳变。实际执行就能看到。

从结果来看,在 for 循环中多次打印信息,这些信息都打印在同一行,并覆盖前面的输出。而不是换行打印。

通过移动光标实现进度条的效果

下面通过移动光标实现进度条的效果。假设有一个 progressbar.sh 脚本,内容如下:

#!/bin/bashfunction print_chars(){    # 传入的第一个参数指定要打印的字符串    local char="$1"    # 传入的第二个参数指定要打印多少次指定的字符串    local number="$2"    local c    for ((c = 0; c < number; ++c)); do        printf "$char"    done}declare -i end=50for ((i = 1; i <= end; ++i)); do    printf "e[80D["    print_chars "#" $i    print_chars " " $((end - i))    printf "] %3d%%" $((i * 2))    sleep 0.1sdoneecho

这个脚本定义了一个 print_chars 函数,可以多次打印同一个字符。

printf "e[80D[" 语句把光标左移 80 列。由于这个进度条的字符总长度小于 80,会移动最左边,总是从第一列开始输出。

在 ‘80D’ 后面的 ‘[’ 字符是进度条的开头第一个字符。

print_chars "#" $i 语句递增打印多个 # 字符,形成进度条往前移动的效果。

print_chars " " $((end - i)) 语句打印多个空格,填充到指定的最后一列,让进度条的结束字符总是打印在同一列。

printf "] %3d%%" $((i * 2)) 语句打印进度条的结束字符 ]、以及进度条百分比。

sleep 0.1s 语句暂停 0.1 秒,避免执行过快,看不到进度条的移动效果。

执行 progressbar.sh 脚本的结果如下:

$ ./progressbar.sh[##################################################] 100%

这里不是动图,看不到进度百分比跳变。实际执行就能看到。

使用 ANSI 转义码清屏、清除字符

下面的 ANSI 转义码可以用于清屏、清除光标往后的字符。

79e89a99a9850ea7cc620fafe0cd5abc.png

ANSI转义码清除终端字符

注意:上面的 J、K 都是大写字母。

具体举例说明如下:

$ printf "123456789e[5De[K"1234$ printf "123456789e[5De[1K"     6789$ printf "123456789e[5De[2K"

printf "123456789e[5De[K" 命令先光标左移 5 列,停在字符 5 的位置,然后用 e[K 转义码从光标位置往后清除所有字符,只保留了前面的 1234 字符串。

printf "123456789e[5De[1K" 命令使用 e[1K 转义码从光标位置往前清除所有字符,只保留了后面的 6789 字符串。

printf "123456789e[5De[2K" 命令使用 e[2K 转义码清除光标所在的整行内容,输出内容为空。

34c72d7a3c9dd07d389e5ee9dd17c0a3.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值