
看到“caveman”这个词我第一反应不是史前人类而是最近在倒腾个人 CLI 工具集合时反复用到的一种终端信息展示方式。你大概率也见过这类东西——安装 Homebrew 时终端里冒出来的大号字体图标或者某些开源项目 README 里用 ASCII 字符拼出来的项目名横幅。它们看起来有点复古却意外地有辨识度能把一段干巴巴的安装说明变成一个有记忆点的开场白。这种“终端艺术字渲染”的思路我习惯管它叫 caveman 风格——本质上是让终端回归最朴素的字符画时代却能解决很实际的信息层级问题。这篇文章不打算写成某个具体库的 API 文档而是分享我用 caveman 思路在终端里做文本渲染的整套实操方案从底层原理到 Shell 脚本实现从安装脚本整合到翻车避坑全都覆盖。如果你正在折腾自动化安装脚本、CLI 工具欢迎页或者只是想让自己的 dotfiles 仓库看起来没那么无聊这篇值得花十分钟看完。1. 从 Homebrew 式的安装界面说起终端艺术字为什么值得折腾我第一次意识到终端艺术字的价值是在写一个 dotfiles 安装脚本的时候。脚本本身逻辑很简单克隆仓库、备份旧配置、建立软链接、输出完成提示。但等我跑完一遍发现整个过程无聊到了极点——屏幕上全是普通的等宽字体输出和apt install没有任何区别完全没有“这是我的项目”的辨识度。后来我试着在脚本开头加了一个用 ASCII 字符拼出来的项目名横幅印象立刻不一样了。那种感觉就像给命令行界面加了一个“品牌页”启动脚本的瞬间会让人多看一眼而不是直接滚动过去。1.1 终端艺术字解决的不是美观问题是信息层级问题很多人觉得终端艺术字是纯装饰其实不完全对。在纯文本界面里所有信息都以相同字体、相同颜色、相近行间距呈现用户很难第一时间判断哪里是重点。安装脚本尤其麻烦——用户需要知道“现在正在执行哪一步”“程序处于什么阶段”如果全部用普通文字视线会被淹没。Caveman 式的渲染提供了一种廉价有效的分层手段大号字符横幅代表“阶段开始”普通文本代表“过程信息”缩进代码块代表“可执行内容”。我在实际脚本里把这三层分得很清楚用户跑起来之后甚至不需要读说明文字扫一眼字形就知道处于哪个阶段。这种利用版式而非语言传递状态的方式在终端里相当好用。1.2 我手写 ASCII 横幅踩的第一个坑对齐问题其实最开始的方案比 caveman 粗暴得多——我打算手写横幅。就是用空格、_、|这种字符直接拼出几个大写字母。写单个字符不难难的是字母之间的横向对齐。我拿一个 6 行高的字母表给每个字符固定宽度然后手动拼接MYPROJECT这个字符串结果不同字体风格混在一起之后字母和字母之间总是忽宽忽窄看起来像是小时候用贴纸拼名字但贴歪了的效果。试了两次之后我放弃了纯手工方案开始寻找系统性的办法。这个转变很关键与其每个人为地维护一张字符画表不如定义一个统一的字符位图结构让程序自动完成对齐和拼接。这其实就是 caveman 这类终端渲染工具底层在做的事情也是我后来在 Shell 脚本里完整复现的一套机制。2. 终端渲染的底层原理每个字符背后都是一张字符位图要理解 caveman 这类工具先得知道一个事实终端里显示的大号 ASCII 艺术字本质上不是“字体”而是“位图画法”。每个字符被拆解成固定行高、固定列宽的单元格每个单元格要么填充字符通常是█、#、*要么留空。渲染的时候程序把你的输入字符串逐字符映射成对应的位图然后按行拼接起来输出。2.1 字符位图的数据结构在我自己实现的版本里每个字符就是一个二维数组用 0 和 1 表示填充状态。比如字母A可以表示成 5 行 5 列的位图01110 10001 10001 11111 10001填充的位置在渲染时替换成高亮字符空的位置替换成空格。这样每个字符的宽度固定为 5行高固定为 5任何字符串按顺序拼接都不会错位。最核心的一点所有字符必须遵循相同的网格尺寸否则就会出现我前面说的“忽宽忽窄”问题。2.2 ANSI 控制序列让横幅带颜色纯字符画有一定复古味道但要让它更像“品牌页”还得加颜色。终端通过 ANSI 转义序列控制字体样式最基础的是 16 色方案格式是printf \033[31m # 红色 printf \033[0m # 重置进阶一点的是 256 色方案和 24 位真彩色方案。我在实践中通常使用 256 色兼容性比真彩色好视觉上也足够丰富。比如我常用一个亮青色38;5;51渲染主标题用灰色38;5;245渲染辅助信息两层一搭配整个终端界面立刻有了层次。2.3 为什么必须用等宽字体终端艺术字对字体有严格要求等宽。只有每个字符在终端里占据完全相同的宽度位图拼接出的画才不会扭曲。绝大多数终端默认就是等宽字体所以这一点通常不需要用户操心但如果你在其他地方排版 ASCII 艺术字比如丢进富文本编辑器效果很可能崩坏。这也是为什么我一直强调终端里跑 caveman别担心字体选择终端外展示就得格外小心。2.4 字体选择block、slant 与 small 的取舍Caveman 这类工具通常会内置多套字体。我用得最多的是 block 体——每个字符完全由实心方块构成视觉冲击力强抗窄终端能力最好偶尔用 slant 体做斜体效果适合展示短单词small 体行高更低适合在空间紧张时使用。个人建议如果你只打算记住一种风格用 block 体不会出错它几乎不会依赖细线与空格的比例在任意等宽终端里都能稳定输出。3. 不换库直接动手在 Shell 脚本里复现 caveman 的核心渲染我平时写自动化脚本以 Bash 为主能不引入外部依赖就不引入。所以我在自己的项目里直接写了一个 Bash 版本的 caveman 渲染器核心代码不算长效果却相当稳。这一节会给出设计思路和可直接抄作业的脚本在干净环境里不需要安装任何额外工具。3.1 用哈希表维护字符位图Bash 的关联数组天然适合做字符位图映射。每个字符对应一个数组数组里的一行就是位图的一行。我用█作为填充符空格作为空白符确保字符间无缝连接。下面是个最小示例declare -A GLYPH_A( [0] ██ [1]█ █ [2]████ [3]█ █ [4]█ █ ) declare -A GLYPH_B( [0]███ [1]█ █ [2]███ [3]█ █ [4]███ )GLYPH_A[0]到GLYPH_A[4]代表 5 行位图每一行的长度要一致这是铁律。渲染一个字符串时把每个字符的对应行依次拼起来render_line() { local line_index$1 shift local result for ch in $; do case $ch in A) result${GLYPH_A[$line_index]} ;; B) result${GLYPH_B[$line_index]} ;; *) result ;; esac done printf %s\n $result }这种方法的好处在于不需要逐字符叠加输出一次循环就能把一行拼完。空格和未知字符统一渲染成 5 个空格保证对齐不被破坏。3.2 自动居中让横幅在任意宽度终端都好看横幅拼出来之后如果直接输出默认左对齐。在宽度较宽的终端里左下角顶格显示的效果其实不够好看。我建议增加居中逻辑先用tput cols获取终端列数再算出当前渲染行去掉 ANSI 转义后的实际长度两侧补上相应个数的空格。center_line() { local raw$1 local width width$(tput cols 2/dev/null || echo 80) local length${#raw} local pad$(( (width - length) / 2 )) printf %${pad}s%s\n $raw }需要注意一点${#raw}在 Bash 中统计的是字符数如果这一行包含 ANSI 颜色转义序列统计长度会和终端显示长度不一致。我们需要在计算长度前先剥离 ANSI 序列或者只在最终输出时才加颜色。我自己习惯的做法是先拼出纯文本行计算完缩进再整行加上颜色这样居中计算永远是正确的。3.3 加颜色封装一套模板走天下把颜色直接封装成两个小函数caveman_banner() { local text$1 local color_code${2:-38;5;51} # 假设 get_glyph 是位图获取函数 for ((row0; rowHEIGHT; row)); do local line for ((i0; i${#text}; i)); do local char${text:i:1} line$(get_glyph $char $row) done printf \033[%sm%s\033[0m\n $color_code $line done }这样无论在安装脚本还是监控脚本里只要调一行caveman_banner DONE 38;5;82就能得到带颜色的完成提示。我实测下来Bash 实现虽然效率不如 C 写的工具但对于几十个字符的横幅输出耗时可以忽略不计。4. 实战整合在安装脚本和 CLI 工具里加上 caveman 信息层有了渲染器接下来的关键问题是在哪里用怎么用才不生硬我从三个实际场景展开都是我自己已经落地跑通的用法。4.1 安装脚本开场横幅建立第一印象我的 dotfiles 安装脚本原本开场是echo Installing dotfiles...改成 caveman 横幅之后整体体验提升非常明显。脚本结构是这样的#!/usr/bin/env bash caveman_banner DOTFILES 38;5;51 echo [INFO] 开始安装配置文件 echo [STEP] 备份已有文件... # 实际安装逻辑 caveman_banner DONE 38;5;82 echo [INFO] 所有配置已就绪这里有个小细节值得留意主标题用大号横幅步骤说明用普通文本完成提示再用一次横幅。两次横幅之间用户能够清晰感知脚本执行的生命周期而不需要盯着每一行日志判断进度。我后来调研过一些用户反馈多数人都表示这种脚本“看起来更像正经工具”而不是随手写的几行 echo。4.2 CLI 工具的子命令提示降低用户记忆成本我还有一个运维小工具内部有十几个子命令。以前用户敲错子命令时终端只能显示一条模糊的错误信息。后来我把“可用命令”列表做成 caveman 风格的模块化输出——每个命令名用普通字体的变色显示下面缩进两格写说明文字整个帮助面板可读性提升了很多。性能方面我也验证过在低配服务器上渲染 5 行字号的横幅生成几十个字符也就是毫秒级完全不会拖慢任何脚本。所以只要不是渲染上万行横幅Shell 方案完全够用。4.3 在 CI 日志里使用一个需要注意的坑但如果你打算在 CI 环境里也用这种方式请先验证日志面板是否保留 ANSI 颜色和等宽字体。我在两个 CI 平台试过一个能完美展示色调和字形另一个把 ANSI 序列剥掉后只剩下毫无规律的字符碎片横幅反而变成了噪音。对于这种场景我的建议是先用环境变量做个开关检测到CItrue时自动降级为纯文本输出别让横幅毁了日志可读性。5. 最容易翻车的四个细节颜色协议、字符宽度、环境检测关于 caveman 的实操经验如果只选一小段最有价值的内容我觉得是“翻车细节”部分。这些坑不是写在文档里的而是我一次次在真实终端里试出来的。5.1 24 位真彩色协议在旧终端直接哑火现在很多终端已经支持 24 位真彩色比如\033[38;2;255;100;0m。这种颜色细腻视觉效果很棒。但问题在于老旧终端、部分 SSH 客户端、以及某些 web 终端模拟器并不支持真彩色它们会把转义序列当普通字符显示结果就是屏幕上突然出现一串看不懂的[38;2;255;100;0m非常破坏体验。稳妥方案是默认使用 256 色必要的时候再检测终端是否支持真彩色。检测方法可以很轻量查环境变量COLORTERM是否包含truecolor或24bit。如果拿不准直接上 256 色就对了它在所有现代终端里都不会出错。5.2 Bash 的${#var}统计 Unicode 字符会踩字符宽度坑我用的填充符是█它在等宽字体里占一个字符的宽度看似没问题。但如果你的位图里有特殊字符比如全角引号或组合字符${#var}统计的长度和终端显示宽度就可能不一致导致居中计算偏差。更恶心的是这种偏差不是稳定常数而是随字符混排方式变化。解决办法是渲染用的填充字符全部使用 ASCII 字符比如#、*、或者像█这种 ECMA-48 明确推荐的块状字符注意它在终端中通常算单宽度。避免在同一个横幅里混排半角和全角字符这是铁律。5.3 不同终端不换行宽度有差异部分终端有自动换行阈值输出刚好达到最后一列时会触发换行导致横幅最右侧被“折”到下一行看起来就像多了一个空格。这种现象在远程终端、tmux 分割窗格场景下尤其容易触发。我目前的策略是渲染行最后不输出多余空格同时把横幅宽度控制在终端宽度减 1 以内也就是最多 79 列。虽然会牺牲一点点视觉宽度但安全性大幅提升。5.4 颜色之外还是留一双“裸眼视角”最后一个建议可能有点意识流但非常重要上色之前先用无色模式渲染一遍看看纯字符画是否依然可读。如果去掉颜色字符轮廓本身表达不了信息那说明字体风格选得不好。好的 caveman 横幅应该做到“开着颜色就惊艳关掉颜色也能用”因为有些用户会在纯文本模式下查看你的日志或脚本输出。6. 从横幅延伸到终端信息层级这不是装饰是界面设计写到这里我想把话题再拉高一点。有人可能觉得终端艺术字只是锦上添花但我自己的体会是它和终端 UI 的信息架构紧密相关尤其当你面对的是需要用户主动参与的交互脚本时。横幅承担的不是美化职责而是“主线引导”——它把屏幕切分成几个视觉区块让用户快速定位当前节点。6.1 终端交互界面的三层信息架构我给自己定了一个终端输出规范任何脚本都遵循三层结构。第一层是 caveman 横幅只出现在关键节点开始、结束、错误第二层是普通高亮文本用于说明当前操作和结果第三层是原始输出或日志直接透传给用户。这套规范和普通 GUI 产品里的“标题栏—正文—注释”异曲同工只是载体从鼠标变成了键盘、从像素变成了字符。6.2 我的个人偏好与建议在字体风格上我偏爱 block 体在颜色系统上我固定使用 256 色系的青、绿、灰三种颜色分别对应主标题、成功、普通信息在位置编排上所有横幅统一居中图标类辅助信息右对齐。坚持这套标准之后多个脚本的输出风格达到了高度统一熟悉了一套就等于熟悉了全部。另外我最终还是把 Bash 版渲染器单独抽成了一个脚本文件通过source方式复用。这样任何新写的 CLI 脚本都能一键引入 caveman 渲染能力不需要重复造轮子。写到这儿我手头这套方案基本分享完了。从最开始手写横幅的对齐崩溃到现在在任何脚本里都能轻松加一段有辨识度、兼容性好、可读性强的终端横幅中间没绕太多弯路但每一个细节都是在真实终端里一遍遍跑出来的。如果你最近也在折腾安装脚本或 CLI 工具不妨给输出界面加一层 caveman 风格的文本渲染。相信我效果比你想象中要好。