【动机】
一般, 我们使用 printf 或 echo 输出颜色文本,大致应该这样写:
printf "\033[0;37m%s\033[0m\n" "使用 printf 打印的文本"
echo -e "\e[31;44;5m使用 echo, 结合 -e 选项打印的文本 \e[0m"
echo "\e[31;44;5m仅仅使用 echo, 无 -e 选项则难如所愿 \e[0m"
很明显, 如果每次打印颜色文本, 都要如此录入是很不方便的.
【意图】
将 echo 或 printf 语句中与颜色相关的部分封装成函数, 使用时调用相应函数即可.
【预备知识】
关于 linux shell 颜色书写的格式, 有几点需要说明:
- \033 是 e 的 ascii 码, 故 \033 与 \e 两者可以互换, 看个人喜好;
- 背景色定义: 0 终端默认颜色, 40 黑, 41 红, 42 绿, 43 黄, 44 蓝, 45 紫, 46 青绿, 47 白(灰)
- 前景色定义: 0 终端默认颜色, 30 黑, 31 红, 32 绿, 33 黄, 34 蓝, 35 紫, 36 青绿, 37 白(灰)
- 字体特效: 1高亮, 4下划线, 5闪烁, 7背景取反. 其他值未知.
- \e到m之间的内容, 即以分号分隔的数字字符串(为叙述方便, 暂时称之为颜色字符串)为紧凑格式, 即只要有一个多余空格, 所有颜色属性失效;
- \e[ 后面的参数指定前景色,背景色,字体效果. 由于三者取值范围不同, 故它们的顺序可互换. 但如果明示某个分量为0, 则 0 必须放在最前端, 否则, 其前面的颜色属性一律失效. 例如以下前三行输出是一致的, 而最后两行的某些颜色属性标识是无效的:
echo -e "\e[31;44;5m顺序为 31 44 5 \e[0m"
echo -e "\e[44;31;5m顺序为 44 31 5 \e[0m"
echo -e "\e[5;44;31m顺序为 5 44 31 \e[0m"
echo -e "\e[5;0;31m字体闪烁特效标识 5 无效, 在于背景色 0 位于其后面 \e[0m"
echo -e "\e[5;44;0m字体闪烁特效标识 5, 以及背景色 44(蓝色) 无效, 在于前景色 0 位于最后 \e[0m"
【初步实现】
第一种实现, 过于简单, 仅以此作为后面的铺垫.
新建文件 console.sh
创建函数 _single_color
_single_color(){
local clr=$1
shift
echo -e "\033[${clr}m$@\033[0m"
}
显然, 调用方式为 _color_output <颜色字符串> <文本>
当然直接调用 _color_output 还是很麻烦, 故而针对几个颜色配置, 专门写了几个函数, 以 caption 函数为例:
:<<COMMENT
打印标题文本,
$1: text
用法: caption <text>
COMMENT
caption(){
_single_color "33;1" "$@"
}
【测试】
新建文件 test.sh
# 根据实际文件位置修改路径
# source ./console.sh
way1(){
caption "single text is no problem"
caption -e "\tThe escape character failed"
caption -n "Failed to cancel end-of-line wrap"
echo ".........................."
}
clear
way1
测试结果:

显然, 简单文本没毛病, 但是需要转义, 或者抑制末尾换行就会出现多余打印或者失效了, 因为在 _single_color 内部, echo 把选项参数(-e 和 -n)均当作纯文本显示 .
所以, 修改还是得从 _single_color 内部来.
仍然在 console.sh 中, 新建 _color_output 函数:
:<<COMMENT
按指定颜色输出文本, 一般不做外部调用
$1: 颜色字符串, 最多3个. 中间用分号隔开, 例如 "41;36;4"
$2: 文本字符串
说明: 背景色, 前景色, 效果的顺序可选, 但如果包含0, 应尽量远离 m 放置
因为 0 以前的数值均被忽略
echo 的其余命令行选项仍然有效, 例如 -n -e -E, 但需要写在一起: -ne -nE
注意: 反人类的写法, 例如 -neE, 或者 -neeee 等, 将被视为文本字符串的一部分
COMMENT
_color_output(){
# 逐步 echo, 方可准确定位命令行选项以及文本字符串
echo -ne "\e[${1}m"
local opt
shift 1
if [[ "$1" =~ ^-[neE]{1,2}$ ]];then
# 确保 echo 的原生命令行选项有效, 例如 -n
opt=$1
shift 1
echo "$opt" "$@"
else
# 也要避免误将文本字符串中的 - 当作命令行选项处理
echo "$@"
fi
echo -ne "\e[0m"
}
大体上, 要点是将格式设置字符串, 文本, 恢复字符串逐个 echo, 达到目的.
然后, caption 函数也应做相应更改
caption(){
#_single_color "33;1" "$@"
_color_output "33;1" "$@"
}
【再次测试】
# 根据实际文件位置修改路径
source ./console.sh
way2(){
caption "single text is no problem"
caption -e "\tThe escape character was successfully escaped "
caption -n "The end-of-line break was successfully suppressed"
echo ".........................."
}
clear
way2
结果如下:

可以看到, 转义字符成功转义, 同时 echo 的原生选项被正确识别, 不在被当作文本的一部分; 末尾换行符也通过 -n 选项成功抑制.
【扩展】
仅仅 caption 一个函数显然不够, 此处再给出几个颜色文本输出函数, 后续文章可能会用到, 读者可以按照此规范, 根据需要写更多
menu(){
_color_output "36;1" "$@" # 绿
}
notice(){
_color_output "36;45;1" "$@"
}
tip(){
_color_output "34;1" "$@"
}
success(){
_color_output "33;42;1" "$@"
}
info(){
_color_output "30;1" "$@" # 青绿
}
warn(){
_color_output "33;43;1" "$@" # 黄
}
error(){
_color_output "33;41;1" "$@" # 红
}
critical(){
_color_output "36;41;5" "$@" # 灰白底, 闪烁红
}
测试略.
最后, 如果想将输出文本做一些简单的左右对齐或 居中处理, 该怎么办? 这就是另一篇: “文本对齐打印” 的事了.