在 NodeJS 终端输出有简单样式的文本内容

To: 想要在 NodeJS 终端输出醒目颜色文本的同学,并且你不希望引入如: chalk, colors 等第三方包。

如果你是上面这样的情况,或者说虽然不是这样,但是也想了解一下的话,那就继续看下去吧~。

如何输出醒目颜色的文本

首先你要安装了 node,(😁)毕竟这是针对 NodeJS 的终端输出记录嘛。
当然这个过程中我也查到了直接在 bash 终端输出颜色的方法,如果你用的不是 Windows 的话,也可以直接看Linux/Mac 终端输出颜色

如果你已经安装了 node,那么可以试一下直接在 REPL 中输入以下脚本来查看效果。

1
console.log('\x1b[1;35mCongratulations!🎉 \x1b[32mYou do it!\x1b[0m');

REPL: Read-Eval-Print-Loop, NodeJS的可交互运行环境, 直接在命令行输入 node 敲击回车键即可进入, 要退出时, 可输入 .exit 敲击回车键。

如果你看到输出了: Congratulations!🎉 You do it!
洋红色Congratulations绿色You do it,那么就算初步成功了。

具体操作

你可能会注意到,脚本输出的内容跟 console.log 的参数有点不一样,这不一样的地方,就是 XTerm 控制序列(XTerm Control Sequences)。

但是你可能并不关心这些,所以我把这部分内容放到了后面。你可以直接查看最关心的内容,也就是都有哪些样式可以设置,以及如何设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
\x1b[0m: 默认样式,也可以将设置的其他样式重置为默认。
\x1b[1m: 加粗、明亮
\x1b[2m: 暗淡、半透明【亮度减半】
\x1b[3m: 斜体
\x1b[4m: 下划线
\x1b[5m: 闪烁【xterm不能识别闪烁或者不可见模式】
\x1b[7m: 颜色反转【前景色和背景色反过来】【前景色就是指文本颜色】
\x1b[8m: 不可见【xterm不能识别闪烁或者不可见模式】
\x1b[9m: 中划线【就是删除线了】

# 你应该能看出来 \x1b[ 和 m 是固定的开始和结束
# 所以下面我就只写中间的数字了,【偷个懒😋】

21: 双下划线【ECMA-48 第三版,但是xterm的属性与 ECMA-48 不兼容,是显示正常亮度,同下面的 22】
22: 正常亮度,用来取消加粗和暗淡,也就是取消 1 和 2
23: 非斜体,用来取消 3
24: 非下划线,用来取消 4
25: 稳定,非闪烁,用来取消 5
27: 正常的,非反的,用来取消 7
28: 可见的,用来取消 8
29: 非划线的,用来取消 9
# 嗯,很明显,2x 是用来取消某些样式的
# 而且除了 22 是取消 1 和 2,其他的取消也都是一一对应的

30: 黑色前景 (Black)
31: 红色前景 (Red)
32: 绿色前景 (Green)
33: 黄色前景 (Yellow)
34: 蓝色前景 (Blue)
35: 洋红色前景 (Magenta)
36: 青色前景 (Cyan)
37: 白色前景 (White)
39: 默认前景 (default)
# 3x 就是用来设置前景颜色,也就是文本颜色的

40: 黑色背景 (Black)
41: 红色背景 (Red)
42: 绿色背景 (Green)
43: 黄色背景 (Yellow)
44: 蓝色背景 (Blue)
45: 洋红色背景 (Magenta)
46: 青色背景 (Cyan)
47: 白色背景 (White)
49: 默认背景 (default)
# 4x 可以设置背景色,且颜色值与前景色一一对应

想要混合起来的话,只需要把数字用 ; (分号)分隔开放入 \x1b[m 之间即可。就像最开始示例中的 \x1b[1;35m
当然你也可以完整的一个一个来,比如 \x1b[1m\x1b[35m,如果你不嫌麻烦的话。

更多的颜色

想要更多的颜色展示?16位颜色支持

1
2
90~97: 对应着 30~37,也是设置一样的前景色,不同的是这个的颜色更加亮一些
100~107: 对应 40~47,设置亮一些的背景色

如果你的系统支持的话,其实 90~97 的颜色和 30~37;1 的颜色基本是一样的,这就是最开始说 1 有加粗,也有明亮的作用。
如果同时设置 12 会怎么样?加粗+明亮+半透明,或许不同的终端效果会不一样?也许你应该自己动手试一下。

如果你的终端还支持 88 或 256 位颜色的话,你甚至可以设置 rgb 颜色。

1
2
3
4
5
6
7
8
9
10
// 38;2;r;g;b 使用rgb设置前景色
console.log('\x1b[38;2;255;165;0mOrange Text\x1b[0m');
// 38;5;index 使用颜色索引设置前景色 索引值范围 0 ~ 255
console.log('\x1b[38;5;215mOrange Text\x1b[0m');

// 背景色类似前景色,只需要把 38 改成 48 即可
// 48;2;r;g;b 使用rgb设置背景色
console.log('\x1b[48;2;255;165;0mOrange Background\x1b[0m');
// 48;5;index 使用颜色索引设置背景色 索引值范围 0 ~ 255
console.log('\x1b[48;5;215mOrange Background\x1b[0m');

Linux/Mac 终端命令行输出颜色

其实在 Linux 和 Mac 的终端命令行工具中输出带颜色的文本,和上面讲述的在 NodeJS 中输出几乎是一样的做法。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ echo -e "\x1b[42;37mGreen Background & White Text\x1b[0m"

# 使用 Mac (zsh) 的同学甚至连 -e 参数都可以不要
$ echo "\x1b[42;37mGreen Background & White Text\x1b[0m"

# 或者用 printf
$ printf "\x1b[1;37;41mAlert\x1b[0m\n"

# 其实开头的转义字符可以使用别的
$ printf "\033[1;37;42m The number 033 in octal equals 0x1b in hexadecimal. \033[0m\n"

# 你甚至可以使用 `\e` 来代替(但这在 NodeJS 中行不通)
$ printf "\e[3;36m Backslash+e means invisible character ESC. \e[0m\n"

Windows 实在没办法,
如果你只是想要更改整个控制台的颜色,那么倒还是有一个方法的。
Windows 里有个 color 命令,可以来变更控制台的输出颜色。
你可以运行 color /? 来查看具体命令
这里也给你列出来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ color /?

设置默认的控制台前景和背景颜色
COLOR [attr]

attr 指定控制台输出的颜色属性

颜色属性由两个十六进制数字指定 __ 第一个对应于背景,第二个对应于前景。
每个数字可以为以下任何值:

0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 浅绿色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色

如果没有给定任何参数,此命令会将颜色还原到 CMD.EXE 启动时的颜色。
这个值来自当前控制台窗口、/T 命令行开关或 DefaultColor 注册表值。

如果尝试使用相同的前景和背景颜色来执行 COLOR 命令,
COLOR 命令会将 ERRORLEVEL 设置为 1.

示例:"COLOR fc" 在亮白色上产生淡红色

控制序列

看过了上面的内容之后,让我们看看控制序列。

当一个字符在通过字符映射表转换之前含有以下14个代码之一的时候表明它是一个控制字符.
00(NUL), 07(BEL), 08(BS), 09(HT), 0a(LF), 0b(VT), 0c(FF), 0d(CR), 0e(SO), 0f(SI), 18(CAN), 1a(SUB), 1b(ESC), 7f(DEL).
我们可以通过设置 显示控制字符 模式以允许 07, 09, 0b, 18,1a, 7f 像普通字符一样显示在屏幕上.
另一方面,在 UTF-8 模式下所有位于 00-1f 之间的代码都被认为是控制字符,而不管是否处于 显示控制字符 模式.

一个控制字符会立刻生效,然后被丢弃(即使是在转义序列中间), 之后转义序列才继续处理下一个字符. (在任何情况下, ESC 都表示一个新的转义序列的开始,可能导致 前一个序列的非正常终止, CANSUB 终止任何转义序列.) 可识别的控制字符是BEL, BS, HT, LF, VT, FF, CR, SO, SI, CAN, SUB, ESC, DEL, CSI.他们的功能如下.:

BEL(0x07,^G)铃声;
BS(0x08,^H)后退一格(在行首不起作用);
HT(0x09,^I)跳至下一个制表位.如果后面已没有制表位则跳至行尾;
LF(0x0A,^J),VT(0x0B,^K),FF(0x0C,^L)三者都表示换行;
CR(0x0D,^M)回车并换行;
SO(0x0E,^N)激活 G1 字符集, 如果设置了 LF/NL(新行模式)还要加上回车换行;
SI(0x0F,^O)激活 G0 字符集;
CAN(0x18,^X),SUB(0x1A,^Z)两者都表示中断转义序列;
ESC(0x1B,^[)开始一个新的转义序列;
DEL(0x7F)忽略;
CSI(0x9B)等同于 ESC [;

而这里要讲的控制序列,其实就是 ESC [

ESC 在 ASCII 码中属于不可见字符,其十进制码点是 27,转换为十六进制就是 0x1b,当然你也可以用八进制来表示为 033

这就是上面提到也可以用 \033 来代替 \x1b 的原因。

同时,你也可以在 nodeREPL 中尝试一下:

1
console.log('\033[1;35mCongratulations!🎉 \033[32mYou do it!\033[0m');

CSI Sequence (CSI 序列)
CSI(即 ESC [)序列的动作由其最后一个字符决定,而 m 则是 SGR(Set Graphics Rendition, 设置图形属性)。
所以我们完整的控制序列就是 ESC [ <parameters> m

颜色索引值

上面提到了通过颜色索引值来设置颜色,那么如何确定不同的索引值,具体对应的是什么颜色呢?

  1. 0 ~ 7 是默认使用的终端颜色,在 RGB 标准化之前就经常被设置使用,它就对应了前景色/背景色的 0 ~ 7,黑/红/绿/黄/蓝/洋红/青/白。

  2. 8 ~ 15 则是对应 0 ~ 7 的明亮色,也可以说对应着 90~97100~107

  3. 16 ~ 231 是 RGB 颜色,这 216 个颜色由 R, G, B 三个轴上各 6 个值来确定,也就是把原本的 0 ~ 255 的区间,缩小为 0 ~ 5 的区间,然后由公式 number = 16 + 36 * r + 6 * g + b 来计算出颜色索引值。

  4. 232 ~ 255,是从暗到亮的 24 中灰度颜色,232是黑色,255是白色

参考链接

  • nodejs-colorful-console

  • printing-colorful-text-in-terminal-when-run-node-js-script

  • Linux命令行颜色

  • reset-colors-on-windows-command-line-cmd

  • what-does-a-bash-sequence-033999d-mean-and-where-is-it-explained

  • wiki:ASCII

  • xterm:ctlseqs

  • xterm:8-bit-Control-Characters

  • xterm:CSI-Pm-m

  • ECMA-48

  • stackoverflow:colors-index

  • Linux 控制终端转义和控制序列(转自下面那条)

  • Linux 控制终端转义和控制序列