javen

V1

2023/04/01阅读:21主题:山吹

给终端输出加点颜色

平时使用第三方npm库开发时经常看到终端打印不同颜色的提示信息,觉得美观的同时,你有想过其中的原理吗?

体验

首先,你可以尝试在bash shell中执行以下命令

echo -e "\033[31m前端斟茶兵\033[0m"

或者用node执行js文件

// test.js
console.log('\033[31m前端斟茶兵\033[0m')

两个命令的结果是一样的,都在终端输出了红色的字体,虽然有点粉😂

为什么用\033[31m这种奇怪的字符包裹文本就能显示特殊颜色呢?这时就需要ANSI转义序列登场了。

ANSI转义序列

这是美国国家标准学会在上世纪70年代制定的标准,并在80年代以来的计算机设备上广泛使用,目前DOS和Unix类的终端模拟器基本都支持ASNI转义序列。

根据维基百科的介绍,这是一种带内信号转义序列标准,用于控制视频文本终端上的光标位置、颜色和其他选项。在文本中嵌入确定的字节序列, 大部分以ESC转义字符和"["字符开始,终端会把这些字节序列解释为相应的指令,而不是普通的字符编码

常用的控制类型ASCII码

以下表格列出0-31位用于控制的不可打印ASCII码

名称 ASCII码 八进制 十六进制 C类型转义字符 Ctrl键 描述
BEL 7 007 0x07 \a ^G 终端铃声
BS 8 010 0x08 \b ^H 回退
HT 9 011 0x09 \t ^I 水平Tab
LF 10 012 0x0A \n ^J 换行
VT 11 013 0x0B \v ^K 垂直Tab
FF 12 014 0x0C \f ^L 换页
CR 13 015 0x0D \r ^M 回车
ESC 27 033 0x1B \e ^[ 转义字符

文章开头给出的例子中033其实是转义字符ESC的八进制表示,对应的十六进制表示为0x1b0x1B,C语言的转义字符为\e,ASCII码为27。 ​

前面的例子改成十六进制或者\e的写法也是可以的,以下3个命令效果一样。

echo -e "\x1b[31m前端斟茶兵\x1b[0m"
echo -e "\x1B[31m前端斟茶兵\x1B[0m"
echo -e "\e[31m前端斟茶兵\e[0m"

注意:最好不要用\e作为转义字符使用,因为不是所有语言和编译器都支持它,比如Python。十六进制表示有时也会有问题,比如下文说到的重置状态。重置状态的转义序列的十六进制表示为\x1bc,它是一个合法的十六进制数,所以终端会把它作为一个整体解析,没有起到\033c的重置作用。

后文会用ESC表示\033或者\x1b

ESC@A-Z[\]^_范围的字符(C1控制字符5)组合,我们称之为转义序列。 ​

部分ANSI转义序列

序列 C1控制字符 名称 描述
ESCN 0x8e SS2 - Single Shift Two 下一个字符从G2可打印字符集选择一个字符
ESCO 0x8f SS3 - Single Shift Three 下一个字符从G3可打印字符集选择一个字符
ESCP 0x90 DCS - 设备控制字符串(Device Control String) 控制设备
ESC[ 0x9b CSI - 控制序列导入器(Control Sequence Introducer) 大部分有用的序列
ESC\ 0x9c ST - 字符串终止(String Terminator) 终止其他控件(APC、DCS、OSC、PM和SOS)中的字符串
ESC] 0x9d OSC - 操作系统命令(Operating System Command) 启动操作系统使用的控制字符串
ESCX 0x98 SOS - 字符串开始(Start of String) 引用由ST终止的一串文本的参数
ESC^ 0x9e PM - 私有消息(Privacy Message)
ESC_ 0x9f APC - 应用程序命令(Application Program Command)
ESCc RIS - 重置为初始状态(Reset to Initial State) 重置图形格式,清除制表符,重置为默认字体等

重置状态的转义序列八进制表示为\033c,十六进制表示为\x1bc,但是\x1bc是个合法的十六进制数,所以它不会像

CSI序列

ESC[的组合我们称为CSI,以CSI为开头的序列则称为CSI序列,又叫ANSI控制序列。

后文会用CSI表示ESC[

CSI + 0或n个参数字节 + 0或n个中间字节 + 1个最终字节组成了CSI序列。

CSI序列后半部分的字符范围

组成部分 字符范围 ASCII
参数字节 0x30-0x3F 0-9:;<=>?
中间字节 0x20-0x2F 空格、!"#$%&'()*+,-./
最终字节 0x40-0x7E @A-Z[\]^_a-z{

CSI序列会忽略超出0x20–0x7E范围的字符。 ​

CSI序列后半部分不同的组合代表着不同的功能,这里列举一些例子。

光标控制

CSI序列 描述
CSIH 光标移至(0, 0)位置
CSInA 光标上移n行
CSInB 光标下移n行
CSInC 光标右移n列
CSInD 光标左移n列
CSInE 光标移至下一行行首,下移n行
CSInF 光标移至上一行行首,上移n行
CSInG 光标移至第n列
CSIn;mH
CSIn;mf 光标移至n行m列
CSIs 保存光标位置,非标准,建议用ESC7
CSIu 恢复上次保存的光标位置,非标准,建议用ESC8

擦除功能

CSI序列 描述
CSIJ 清除屏幕
CSI0J 清除光标到屏幕末尾的内容
CSI1J 清除光标到屏幕初始的内容
CSI2J 清除整个屏幕
CSIK 清除当前行
CSI0K 清除光标到行末的内容
CSI1K 清除光标到行首的内容
CSI2K 清除整行

屏幕模式

CSI序列 描述
CSI=0h 40 x 25 黑白(文本)
CSI=1h 40 x 25 彩色(文本)
CSI=2h 80 x 25 黑白(文本)
CSI=3h 80 x 25 彩色(文本)
CSI=4h 320 x 200 4色(图像)
CSI=5h 320 x 200 黑白(图像)
CSI=6h 640 x 200 黑白(图像)
CSI=7h 允许换行
CSI=13h 320 x 200 彩色(图像)
CSI=14h 640 x 200 彩色(16色图像)
CSI=15h 640 x 350 黑白(2色图像)
CSI=16h 640 x 350 彩色(16色图像)
CSI=17h 640 x 480 黑白(2色图像)
CSI=18h 640 x 480 彩色(16色图像)
CSI=19h 320 x 200 彩色(256色图像)
CSI={value}h 将屏幕宽高或类型改成value对应的模式
CSI={value}l 最终字符是小写L,重置value对应的模式,如果value为7,则禁止换行

键盘字符串

ESC[{code};{string};{...}p的模式可以将某个键盘键位重定义成指定的字符串。具体可以参考资料2。 ​

从上面可以看出,当最终字符为A-H时,表示光标移动。当最终字符为JK时,表示擦除。当最终字符为h,参数字节为=时,表示屏幕模式。 ​

而当最终字符为m,中间没有参数或者参数以;分隔的CSI序列,就是我们要重点讲的SGR

SGR

Select Graphic Rendition,选择图形再现,可以设置终端文本样式,包括颜色、粗细、斜体、下划线等。

常用样式设置

下表列举了除颜色外的常用样式的CSI序列

CSI序列 重置序列 描述
CSI0m 重置所有样式模式
CSI1m CSI21m 粗体
CSI2m CSI22m 弱化
CSI3m CSI23m 斜体
CSI4m CSI24m 下划线
CSI5m CSI25m 缓慢闪烁
CSI6m CSI26m 快速闪烁
CSI7m CSI27m 反显,前景色背景色交换
CSI8m CSI28m 隐藏
CSI9m CSI29m 划除

8/16色

颜色 前景色代码 背景色代码
30 40
31 41
绿 32 42
33 43
34 44
品红 35 45
36 46
37 47
默认 39 49
重置 0 0

30-37是标准的8种标准前景色,40-47是对应的背景色,39或49可以重置颜色。没有提及的38和48是用来设置256色和RGB颜色的,后文会说。 ​

对于支持aixterm规范3的终端,还有另外的8种高强度色。

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

如果要设置前景是红色,背景是白色的粗体文本,可以这么写

\033[1;31;47m

对不同终端来说,同一个颜色代码显示出来的颜色可能会有差异。

256色

CSI序列 描述
CSI38;5{ID}m 设置前景色
CSI48;5{ID}m 设置背景色

5表示使用256色,ID是0-255中的一个值,可以理解为颜色的序号。

256色的计算方式

ID 对应关系
0-7 对应8/16色中的标准色30-37
8-15 对应8/16色中的高强度色90-97
16-231 216种,计算公式:16 + 36 × R + 6 × G + B (0 ≤ R, G, B ≤ 5)
232-255 24阶灰度色

RGB色

CSI序列 描述
CSI38;2;{R};{G};{B}m 设置前景色
CSI48;2;{R};{G};{B}m 设置背景色

2表示使用RGB色,支持真彩色(24位RGB)的终端才能使用。R、G、B取值在0-255区间,做前端的同学应该很熟悉了。

\033[0m

回想文章开头的例子,我们用到了\033[0m,就是为了重置所有样式。更简洁的写法是\033[m,因为没有参数的话会默认参数为0。 ​

当然,如果只设置了颜色或者只想重置颜色,可以用\033[39m或者\033[49m来重置。 ​

不重置样式可以吗?当然可以。但是如果不重置样式,又没有设置新样式的话,后面的输出都会继承前面的样式。比如node运行下面的代码

// test.js
console.log('\033[31m前端斟茶兵');
console.log('前端斟茶兵');

第二句打印也会有颜色,这可能不是我们想要的。

chalk

chalk是非常有名的在终端显示颜色的nodejs库,它是怎么做的呢?

调试发现,它封装了一个叫ansi-styles6的库,这个库同样遵循转义序列的规范,具体可以看代码。不过,它使用unicode \u001B 来表示ESC

总结

  1. 转义字符ESC有多种表示方式:\033\x1b\x1B\e\u001B
  2. ESC和一些特定字符组合成为转义序列(Escape Sequence),ESC[组成CSI(控制序列导入器);
  3. CSI序列可以控制终端行为,当最终字符为m,中间参数用;分隔,则是SGR模式,可以设置终端文本的颜色、粗细等样式,一般写成\033[{参数1};{参数2};{...参数n}m这种格式;
  4. 不同的终端对CSI序列的支持程度不一样,上面讲到的样式可能看不到效果,颜色显示可能也有差异;

最后,上一个效果图

是不是很酷啊!

参考资料

  1. What does printf("\033c") mean: https://stackoverflow.com/questions/47503734/what-does-printf-033c-mean/47503782
  2. ANSI Escape Sequences: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
  3. ANSI转义序列:https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97
  4. aixterm规范:https://sites.ualberta.ca/dept/chemeng/AIX-43/share/man/info/C/a_doc_lib/cmds/aixcmds1/aixterm.htm
  5. C1控制字符:https://zh.wikipedia.org/wiki/C0%E4%B8%8EC1%E6%8E%A7%E5%88%B6%E5%AD%97%E7%AC%A6#C1%E6%8E%A7%E5%88%B6%E5%AD%97%E7%AC%A6%E9%9B%86
  6. asni-styles: https://github.com/chalk/ansi-styles

分类:

前端

标签:

前端

作者介绍

javen
V1