欢迎光临
我们一直在努力

终端的颜色控制

简介

终端的颜色控制在两个标准中有定义。一个是ISO/IEC 6429:1992,另一个是ECMA-48。前者的最新版本是Edition 3, 1992,后者的最新版本是5th edition, June 1991。

有趣的是,ISO和ECMA的总部都在日内瓦,而IEC和ITU也是。

这两个标准中关于演示的定义部分为SGR (SELECT GRAPHIC RENDITION)。在 ISO/IEC 6429:1992 中的章节为 8.3.118 SGR – SELECT GRAPHIC RENDITION;在ECMA-48中的章节为:8.3.117 SGR – SELECT GRAPHIC RENDITION。前者是收费的,后者可以从这里下载。

两个标准关于SGR的定义是一致的。在下面的标准引用中,如无特殊说明,均为ECMA。

SGR标准是跨平台的终端控制协议,它既不是操作系统(如 Linux)的组成部分,也不是命令解释器(如 Bash)的功能。尽管本文中的实验是基于Linux Bash的。这也是本文的题目不是“Linux的颜色控制”或“Bash的颜色控制”的原因。

SGR 的格式

在ECMA中,关于SGR的说明如下:

Notation: (Ps…)
Representation: CSI Ps… 06/13
Parameter default value: Ps = 0
# 下面是参数表,此处省略

其中:

  • CSI表示CONTROL SEQUENCE INTRODUCER,CSI是控制序列的起始标志,为Esc+[键,即\\e[。
  • Ps表示Parameters,Ps…表示可以多个参数。( )表示为可选参数。
  • 06/13 就是字符m,即0x6D。这是最关键的,m表示此CSI序列为SGR。
  • 参数间的分隔符是;,是在标准的5.4.2节说明的,Parameter sub-strings are separated by one bit combination 03/11.。

$ echo -e '\\x6d'
m
$ printf '%d\\n' 0x6d
109

在这里插入图片描述

以下图示来自VT100的帮助文档: 在这里插入图片描述

简单实验

SGR的参数可以从标准中获得,但最方便的还是从console_codes的man page中获得。 在这里插入图片描述 也可参考下表:

表1:SGR属性表

参数含义备注
0 Attributes off / Reset all 默认值
1 Bold or increased intensity
2 Faint, decreased intensity 较少支持
3 Italic 不一定所有终端支持
4 Underscore
5 Blink (slow)
6 Blink (rapid) 较少支持
7 Negative (reverse) image / Inverse 前景色和背景色互换
8 Conceal / Hide 文字不可见(但可选中)
9 Crossed-out / Strike-through 删除线
21 Bold off or double underline 不同终端解释不同
22 Normal intensity (neither bold nor faint)
23 Not italic
24 Underline off
25 Blink off
27 Positive (normal) image 取消反显
28 Reveal 取消隐藏
29 Not crossed-out
30–37 Set foreground color 非ECMA标准,来自DEC实现
38 Set foreground color (extended) 后接;5;n(256色)或;2;r;g;b(RGB)
39 Default foreground color
40–47 Set background color 非ECMA标准,来自DEC实现
48 Set background color (extended) 后接;5;n(256色)或;2;r;g;b(RGB)
49 Default background color
90–97 Set bright foreground color
100–107 Set bright background color

据此,我们可以做几个简单的实验:

echo;echo
printf '\\e[31mRed text\\e[m\\n'
printf '\\e[31m\\e[44mRed text with blue background\\e[m\\n'
printf '\\e[31;44mRed text with blue background\\e[m\\n'
printf '\\e[31;44;1mBold Red text with blue background\\e[m\\n'
printf '\\e[31;44;4mUnderline Red text with blue background\\e[m\\n'
printf '\\e[31;7mNegative Red text with blue background\\e[m\\n'
printf '\\e[91mBright Red text with blue background\\e[m\\n'
printf '\\e[31;9mStrike-through Red text with blue background\\e[m\\n'
echo;echo

输出如下: 在这里插入图片描述

以上的printf也可以替换为echo -en

高阶实验

在简单实验一节的表1:SGR属性表中,我们看到参数38和48支持256色。

在console_codes的man page中描述道:

Commands 38 and 48 require further arguments: ;5;x 256 color: values 0…15 are IBGR (black, red, green, … white), 16…231 a 6x6x6 color cube, 232…255 a grayscale ramp ;2;r;g;b 24-bit color, r/g/b components are in the range 0…255

因此可利用其做一些复杂的演示:

# 256色背景
for i in {0..256} ; do echo -en "\\e[48;5;${i}m${i}\\e[0m" ; done ; echo
# 256色前景
for i in {0..256} ; do echo -en "\\e[38;5;${i}m${i}\\e[0m" ; done ; echo

其输出如下: 在这里插入图片描述

以下脚本256colors.sh让输出排列更整齐:

#!/bin/bash

declare -i width
declare bg_or_fg

if [[ $# == 0 ]]; then
echo Usage: $0 width fg|bg
exit 0
fi

width=$1
[[ $width == 0 ]] && width=16

if [[ $2 == "fg" ]]; then
bgfgcode=38;
elif [[ $2 == "bg" ]]; then
bgfgcode=48;
else
bgfgcode=38;
fi

for i in {0..255}
do
printf "\\e[${bgfgcode};5;${i}m%4d\\e[0m" $i
(( (i + 1) % $width == 0 )) && echo
done

在这里插入图片描述 不过这个显示只是花哨而已,最正确的方式还是要参考Bash tips: Colors and formatting (ANSI/VT100 Control sequences)中的256-colors.sh。其代码核心部分如下:

for fgbg in 38 48 ; do # Foreground / Background
for color in {0..255} ; do # Colors
# Display the color
printf "\\e[${fgbg};5;%sm %3s \\e[0m" $color $color
# Display 6 colors per lines
if [ $((($color + 1) % 6)) == 4 ] ; then
echo # New line
fi
done
echo # New line
done

这其中有几个magic number,解读一下。

  • 38和48分别表示前景和背景
  • 6,是因为对于256色,16-231是6x6x6 color cube。因此6就是每行显示的颜色数
  • 4, 是因为对于256色,0-15是BGR,为了保证颜色16在行首,首行显示4个颜色后就必须折行

这个效果就完美了。(为方便查看,我把纵向的输出并排放了) 在这里插入图片描述 这也方便你理解作者以下代码的含义了:

# 渐变
for i in {16..21} {21..16} ; do echo -en "\\e[38;5;${i}m#\\e[0m" ; done ; echo

其他

颜色只是终端的能力之一,要拓展到终端层面,还可以看下terminfo, tput的帮助。

下面给出几个命令:

$ echo $TERM
xterm
$ BOLD=$(tput bold)
$ echo "$BOLD"|od -bc
0000000 033 133 061 155 012
033 [ 1 m \\n
0000005
# 为何红色为1,请看terminfo(5) 的 Color Handling部分
$ RED=$(tput setaf 1)
$ echo "$RED"|od -bc
0000000 033 133 063 061 155 012
033 [ 3 1 m \\n
0000006
$ tput reset
$ tput init
# 显示红色
$ printf "\\e${RED}red\\e[m\\n"
red
## 显示红色粗体
$ printf "\\e${RED}\\e${BOLD}red\\e[m\\n"
red

参考 man page

  • console_codes
  • infocmp
  • tput
  • terminfo
  • dir_colors

参考文档

  • ISO/IEC 6429:1992
  • ECMA-48
  • Bash tips: Colors and formatting (ANSI/VT100 Control sequences)
  • LS_COLORS generator
  • VT100 User Guide: SGR – Select Graphic Rendition
  • ANSI escape code
赞(0)
未经允许不得转载:171主机测评 » 终端的颜色控制
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址