乐趣区

关于linux: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 的模式,少输出一些字符。

具体测试如下:

$ echo -e “e[31m”

<span style=”color:red;”>$ echo -e “e[0m”</span>

$

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

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

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

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

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

举例说明如下:

$ echo -e “e[31mThis is a red string.e[0m”
<span style=”color:red;”>This is a red string.</span>
$

在下面命令中,\e[31m 是一个 ANSI 本义码,示意设置终端字符色彩为红色。

\e[0m 也是一个 ANSI 本义码,示意重置终端的色彩属性,会复原成原来的色彩。

在这两个本义码两头的字符串会显示在终端上。

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

设置终端字符色彩的 ANSI 本义码

上面具体阐明设置终端字符色彩的 ANSI 本义码,其根本格局如下:

Esc[Value;...;Valuem

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

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

本义码最初以 m 字符结尾。

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

色彩值 色彩
30 彩色
31 红色
32 绿色
33 黄色
34 蓝色
35 紫色
36 青色
37 红色

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

色彩值 色彩
40 彩色
41 红色
42 绿色
43 黄色
44 蓝色
45 紫色
46 青色
47 红色

设置字符属性的值如下:

属性值 属性含意
0 重置所有属性,蕴含字符色彩
1 设成粗体
4 增加下划线
5 关上闪动
7 色彩反转
8 显示不可见的文本

具体举例如下:

$ echo -e “e[31;44mFg color: Red. Bg color: Blue.e[0m”
<span style=”color:red;background-color:blue;”>Fg color: Red. Bg color: Blue.</span>
$ echo -e “e[44;31mFg color: Red. Bg color: Blue.e[0m”
<span style=”color:red;background-color:blue;”>Fg color: Red. Bg color: Blue.</span>

能够看到,\e[31;44m\e[44;31m 这两个本义码设置的字符色彩成果是一样的。

所给的前景色、背景色没有要求先后顺序。

目前的大部分终端都反对 256 色,能够应用 Esc[38;5;Valuem 来设置终端字符为 256 色。

这里的 Value 取值是 0-255。

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

具体的色彩取值能够查看 256 色的色彩表。网上的很多文章都有阐明。这里不再列举。

应用 ANSI 本义码挪动终端光标

ANSI 本义码能够用来挪动终端的光标地位,从而扭转字符的输入地位。

具体举例如下:

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

在这个命令中,\e[4D 本义码示意把光标往左挪动 4 列。

能够看到,光标挪动 4 列后,位于字符 6 所在的地位,从新输入 abc,笼罩了原来的 678 三个字符。

挪动光标的具体本义码阐明如下:

本义码 含意
Esc[nA 光标上移 n 行,列数不变。挪动到终端最上边后不再挪动
Esc[nB 光标下移 n 行,列数不变。挪动到终端最下边后不再挪动
Esc[nC 光标右移 n 列,行数不变。挪动到终端最左边后不再挪动
Esc[nD 光标左移 n 列,行数不变。挪动到终端最右边后不再挪动
Esc[nE 光标下移 n 行,列数变到行首
Esc[nF 光标上移 n 行,列数变到行首
Esc[Line;ColumnH 把光标挪动到指定的行数和列数。如果不提供值,默认值为 0
Esc[ColumnG 把光标挪动到第 Column 列,以后行数放弃不变
Esc[s 保留以后光标地位,后续能够用 Esc[u 跳到保留的地位
Esc[u 跳转到 Esc[s 所保留的光标地位
Esc[?25l 暗藏光标(在 25 前面是小写字母 l)
Esc[?25h 显示光标

下面所说的终端地位指的是终端可见的窗口地位,不包含缓冲区地位。

即,窗口显示不会产生滚动,只在以后可见的窗口区域跳转光标。

留神 :因为 echo 命令默认会输入换行符,导致挪动光标后再次换行,会对光标挪动成果造成烦扰。

在测试挪动光标的本义码时,倡议用 printf 命令测试。该命令默认不会输入换行符。

因为 bash 外面须要按下回车才执行命令,会影响光标的左右挪动成果,倡议在 printf 本身输入的内容中左右挪动光标。

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

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

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

应用 printf 命令测试如下:

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

能够看到,应用 \e[D\e[0D\e[1D 往左挪动光标,而后输入字符 a,都是笼罩同一个字符 9。

这三个本义码的光标挪动成果雷同。

$ printf "123456789\e[4Da\n"
12345a789
$ printf "123456789\e[4D\e[Ca\n"
123456a89
$ printf "123456789\e[4D\e[0Ca\n"
123456a89
$ printf "123456789\e[4D\e[1Ca\n"
123456a89

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

\e[C\e[0C\e[1C 都是往右挪动光标到下一列,到字符 7 的地位,输入字符 a,笼罩了字符 7。

通过挪动光标实现进度百分比的成果

咱们能够通过挪动光标实现进度百分比的成果。假如有一个 progress.sh 脚本,内容如下:

#!/bin/bash

for ((i = 0; i <= 100; ++i)); do
    printf "\e[5D%3d%%" $i
    sleep 0.1s
done
echo

这里应用 printf 命令进行输入,以便格式化字符串。

printf 命令也是应用 \e 来示意 escape 字符。

\e[5D 本义码示意把光标左移 5 列。

因为所输入的字符不超过 5 个字符,每次光标左移 5 列,都会挪动到最右边,从第一列开始输入。

那么前面输入的内容会笼罩后面输入的内容,达到在同一行反复输入的成果。

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

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

$ ./progress.sh
100%

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

从后果来看,在 for 循环中屡次打印信息,这些信息都打印在同一行,并笼罩后面的输入。而不是换行打印。

通过挪动光标实现进度条的成果

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

#!/bin/bash

function print_chars()
{
    # 传入的第一个参数指定要打印的字符串
    local char="$1"
    # 传入的第二个参数指定要打印多少次指定的字符串
    local number="$2"
    local c
    for ((c = 0; c < number; ++c)); do
        printf "$char"
    done
}

declare -i end=50
for ((i = 1; i <= end; ++i)); do
    printf "\e[80D["
    print_chars "#" $i
    print_chars " " $((end - i))
    printf "] %3d%%" $((i * 2))
    sleep 0.1s
done
echo

这个脚本定义了一个 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 本义码能够用于清屏、革除光标往后的字符。

本义码 含意
Esc[2J 革除屏幕显示的内容。在 Ubuntu 上测试,光标地位会放弃不变
Esc[K 革除从光标地位到行尾的所有字符(包含光标下的字符)
Esc[1K 革除从光标地位到行首的所有字符(包含光标下的字符)
Esc[2K 革除光标所在的整行内容

留神 :下面的 J、K 都是大写字母。

具体举例说明如下:

$ printf "123456789\e[5D\e[K\n"
1234
$ printf "123456789\e[5D\e[1K\n"
     6789
$ printf "123456789\e[5D\e[2K\n"

printf "123456789\e[5D\e[K\n" 命令先光标左移 5 列,停在字符 5 的地位,而后用 \e[K 本义码从光标地位往后革除所有字符,只保留了后面的 1234 字符串。

printf "123456789\e[5D\e[1K\n" 命令应用 \e[1K 本义码从光标地位往前革除所有字符,只保留了前面的 6789 字符串。

printf "123456789\e[5D\e[2K\n" 命令应用 \e[2K 本义码革除光标所在的整行内容,输入内容为空。

退出移动版