首页 给终端输出加点颜色
文章
取消

给终端输出加点颜色

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

体验

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

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

或者用node执行js文件

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

两个命令的结果是一样的,都在终端输出了红色的字体,虽然有点粉😂 image.png 为什么用\033[31m这种奇怪的字符包裹文本就能显示特殊颜色呢?这时就需要ANSI转义序列登场了。

ANSI转义序列

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

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

常用的控制类型ASCII码

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

名称ASCII码八进制十六进制C类型转义字符Ctrl键描述
BEL70070x07\a^G终端铃声
BS80100x08\b^H回退
HT90110x09\t^I水平Tab
LF100120x0A\n^J换行
VT110130x0B\v^K垂直Tab
FF120140x0C\f^L换页
CR130150x0D\r^M回车
ESC270330x1B\e^[转义字符

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

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

1
2
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控制字符名称描述
ESCN0x8eSS2 - Single Shift Two下一个字符从G2可打印字符集选择一个字符
ESCO0x8fSS3 - Single Shift Three下一个字符从G3可打印字符集选择一个字符
ESCP0x90DCS - 设备控制字符串(Device Control String)控制设备
ESC[0x9bCSI - 控制序列导入器(Control Sequence Introducer)大部分有用的序列
ESC\0x9cST - 字符串终止(String Terminator)终止其他控件(APC、DCS、OSC、PM和SOS)中的字符串
ESC]0x9dOSC - 操作系统命令(Operating System Command)启动操作系统使用的控制字符串
ESCX0x98SOS - 字符串开始(Start of String)引用由ST终止的一串文本的参数
ESC^0x9ePM - 私有消息(Privacy Message) 
ESC_0x9fAPC - 应用程序命令(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-0x3F0-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=0h40 x 25 黑白(文本)
CSI=1h40 x 25 彩色(文本)
CSI=2h80 x 25 黑白(文本)
CSI=3h80 x 25 彩色(文本)
CSI=4h320 x 200 4色(图像)
CSI=5h320 x 200 黑白(图像)
CSI=6h640 x 200 黑白(图像)
CSI=7h允许换行
CSI=13h320 x 200 彩色(图像)
CSI=14h640 x 200 彩色(16色图像)
CSI=15h640 x 350 黑白(2色图像)
CSI=16h640 x 350 彩色(16色图像)
CSI=17h640 x 480 黑白(2色图像)
CSI=18h640 x 480 彩色(16色图像)
CSI=19h320 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 重置所有样式模式
CSI1mCSI21m粗体
CSI2mCSI22m弱化
CSI3mCSI23m斜体
CSI4mCSI24m下划线
CSI5mCSI25m缓慢闪烁
CSI6mCSI26m快速闪烁
CSI7mCSI27m反显,前景色背景色交换
CSI8mCSI28m隐藏
CSI9mCSI29m划除

8/16色

颜色前景色代码背景色代码
3040
3141
绿3242
3343
3444
品红3545
3646
3747
默认3949
重置00

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

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

颜色前景色代码背景色代码
亮黑90100
亮红91101
亮绿92102
亮黄93103
亮蓝94104
亮品红95105
亮青96106
亮白97107

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

1
\033[1;31;47m

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

256色

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

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

256色的计算方式

image.png

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

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运行下面的代码

1
2
3
// 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序列的支持程度不一样,上面讲到的样式可能看不到效果,颜色显示可能也有差异;

最后,上一个效果图

image.png

是不是很酷啊!

参考资料

  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
本文由作者按照 CC BY-NC-ND 3.0 进行授权

-

N个前端开发实用网站