在Linux终端下搞颜色!

在尝试开发一个控制台程序的时候,接触到了控制台转义字符(Escape Code),可以用于控制光标或达到一些特殊显示效果,比如lolcat.

趁此机会,记录一下这些操作.

CSI序列

CSI(Control Sequence Introducer)是一系列控制控制台下特殊效果的代码序列, 它们以ESC[开始,后接不同的控制参数.
ESC字符即ASCII27,可以用如下方式得到:

  • \e

    1
    echo -e "hello, \e[31mworld\e[0m"
  • \033

    1
    echo -e "hello, \033[31mworld\033[0m"
  • \x1b

    1
    echo -e "hello, \x1b[31mworld\x1b[0m"

以上三种方式完全一致,都是输出普通颜色的hello和红色的world.

顺带一提, 我测试过的语言中,只有C/C++支持\e转义方式.其他语言可以使用八进制或16进制的转义方式实现.一些语言默认使用了16位宽字符,可以用\u实现.

Go语言:

1
2
3
fmt.Println("hello, \033[31mworld\033[0m")
fmt.Println("hello, \x1b[31mworld\x1b[0m")
fmt.Println("hello, \u001b[31mworld\x1b[0m")

Java的字符串转义没有\x:

1
2
System.out.println("\033[31mhello, \033[0m world!");
System.out.println("\u001b[31mhello, \u001b[0m world!");

CSI有很多有趣的控制命令, 如echo -e "\e[?25l"可以隐藏光标, echo -e "\e[H\e[0J"会将光标移动至屏幕左上角并清空屏幕,也就是相当于一次clear.

CSI用于控制显示效果的序列被称为SGR(Select Graphic Rendition),主要包括字体样式和颜色的设置,代码为CSI n m.其中,n为不同的数字代码,例如上面例子中的\e[31m就是设置颜色为红色.n可以同时有多个,以;隔开.

终端以状态机的形式设置这些效果,所以\e[31m设置了红色以后,整个终端的输出会一直编是红色,直到另一个效果覆盖了它,或者这个效果被取消.

字体样式

这里的字体样式是指诸如粗体,斜体,下划线这些显示效果,而不是设置如宋体,Consolas这些字体.

效果如下:

Code Desc Preview
1 粗体(Bold/Bright)
2 黯淡(Dim/Faint)
3 斜体(Italicized)
4 下划线(Underline)
5 闪烁(Blink)
7 反转前景和背景颜色(Inverse)
8 隐藏(Invisible/Hidden)
9 删除线(Strikethrough/Cross-out)

Tips:

  • 如上所说,可以同时设置多种效果

    例如\e[1;3;4;9m可以同时设置粗体,斜体,下划线和删除线

  • 粗体和黯淡,下划线和双重下划线分别是同一类样式,会互相覆盖

  • 隐藏不是不显示内容

    隐藏效果其实只是让文字的前景色和背景色一样,这段文字还是可以被复制的.

    所以千万不要用来用这个效果处理真正敏感的信息,顶多可以用来做一些’反白’的小彩蛋

  • 代码6在标准和终端实现文档里都没有记录,不过测试后,发现它的效果也是闪烁.

  • 这些效果在不同的终端也会有不同的显示效果

    例如,vte下这些效果显示如下:

取消字体样式

如上所说,终端是一个状态机,上面的显示的效果可以由对应的指定取消掉.

Code Desc
0 恢复默认,即取消所有特殊效果,包括之后会提到的设置颜色
22 取消粗体或黯淡效果
23 取消斜体
24 取消下划线
25 取消闪烁
27 取消发转
28 取消隐藏
29 取消删除线

Tips:

  • 基本上,字体样式中的效果代码加上20就是取消这个效果的代码

  • 粗体(1)和黯淡(2)都使用代码22来取消,下划线(4)和双重下划线(21)都使用代码24来取消

  • 虽然,代码25可以取消代码6的闪烁效果,但是26没有实际效果

颜色设置

8色和16色

颜色 前景代码 背景代码
黑色 30 40
红色 31 41
绿色 32 42
黄色 33 43
蓝色 34 44
品红(magenta) 35 45
青色(Cyan) 36 46
灰色 37 47
默认 39 49

16色在8色的基础上,增加了对应的高亮度的颜色

颜色 前景代码 背景代码
灰色 90 100
亮红色 91 101
亮绿色 92 102
亮黄色 93 103
亮蓝色 94 104
亮品红(magenta) 95 105
亮青色(Cyan) 96 106
白色 97 107

这16种颜色在xterm中显示效果如下:

vte下效果如下:

Tips:

  • 代码3949分别用来重置前景和背景色,具体颜色取决于当前终端

  • vscode的颜色取决于当前主题,所以最终效果是不可预期的.

    例如,这是在Lichen Light主题下的显示效果:

256色(8-bit)和24位色

256色使用序列38;5;n48;5;n来设置前景和背景色.n取值为[0,255].

xterm中效果如下:

24位色使用序列38;2;r;g;b48;2;r;g;b设置前景色和背景色.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
for(int i = 0; i < 256; i += 6)
{
for(int j = 0; j < 256; j += 3)
{
printf("\x1b[38;2;%d;%d;255m#\x1b[0m", i, j);
}
printf("\n");
}
return 0;
}

xterm中效果如下:

试着在终端下显示图片:

参考资料

Bash tips: Colors and formatting (ANSI/VT100 Control sequences)

XTerm Control Sequences

ANSI escape code