首页
/ Windows Terminal 的 ansi-color:一个纯 Batch 实现的 ANSI/SGR 颜色矩阵渲染工具

Windows Terminal 的 ansi-color:一个纯 Batch 实现的 ANSI/SGR 颜色矩阵渲染工具

2026-09-06 15:14:33作者:谭伦延

ansi-color 是 Windows Terminal 仓库中一个用 Windows 命令脚本(Batch)编写的诊断工具,它能将 SGR(Select Graphic Rendition)属性、前景色和背景色以表格形式渲染到终端,用于直观检查当前终端对 ANSI 转义序列的支持程度。它与仓库中的 ColorTool 工具互补:ColorTool 负责应用和导出配色方案,ansi-color 则负责把任意一组 ANSI 转义序列组合渲染成可视化矩阵,便于编写或调试配色方案时验证渲染效果。

工具定位:与 ColorTool 的分工

src/tools/ColorTool 中,ColorTool 提供 ColorTool.exe -c 命令打印当前已应用配色方案的颜色表,并支持导入 .itermcolors / .json / .ini 格式的方案文件。而 ansi-color 的价值在于它渲染的是任意的 SGR 参数组合——不只是配色方案里的 16 色,还包括粗体、斜体、下划线、闪烁、反显、隐藏、删除线等属性及其与强度的组合。仓库文档明确说明它“为诊断目的以及与查看已应用配色方案的全部颜色相配合”而存在,src/tools/ansi-color/README.md 即该工具的官方说明文档。

快速上手:命令、参数与标志

工具本体是单个命令脚本 ansi-color.cmd,直接运行即可(不带参数时使用脚本自身内嵌的定义文件):

Usage: ansi-color.cmd [flags] [<definition_file>]

   This file echoes a bunch of color codes to the terminal to demonstrate
   how they will render. The `Data Segment` portion of the file defines the
   table layout and allows the user to configure whatever matrix of ANSI
   Escape Sequence control characters they wish. This can also be read from
   an external definition file using the same structure.

   Flags:
     /H  :  This message
     /A  :  Display the ANSI Escape Sequence control characters
     /R  :  Show cell R1C1 reference addressing instead of cell text
     /U  :  Enable UTF-8 support

各标志的完整行为(结合 src/tools/ansi-color/ansi-color.cmd 中的参数解析逻辑):

标志 作用 实现细节
/H 打印帮助信息 调用 :USAGE 例程后以退出码 0 结束
/A 以 Unicode 码位字符(␛)替代真正的 ESC 控制字符,把将要生成的转义序列“原样”打印出来 自动隐含 /U(强制 UTF-8)并禁用进度动画,见 源码 L395-L398
/R 用 Excel 风格的 R1C1 行列引用(如 R3C7)替代单元格文本 单元格固定按最大 7 个字符宽预留(R999C99),见 源码 L478-L482
/U 启用 UTF-8 支持,将控制台代码页切换到 65001 依赖系统自带的 CHCP 工具

参数解析同时接受 - 前缀,这是为了在 PowerShell 中调用时更符合 PowerShell 的参数习惯(源码 L1435-L1438)。脚本本身唯一的“外部依赖”就是 CHCP——它只在你需要显示 Unicode 文本时才会把命令提示符的代码页设为 65001,并在脚本成功结束后自动恢复原代码页(源码 L400-L445L532-L539)。

整个工具以 Windows 命令脚本写成,无需编译,唯一依赖是系统自带的 CHCP。脚本大量使用 Batch “宏”技术(这一技巧源自 DosTips 社区的 Ed Dyreen、Jeb 与 Dave Benham 等人的探索),使得它能在纯批处理环境下生成由独立定义文件描述的复杂表格。

核心机制:脚本自身就是定义文件

ansi-color 一个非常特别的设计是:脚本文件本身就是它自己的定义文件,不需要外部定义文件也能工作。src/tools/ansi-color/ansi-color.cmd 只是一个 UTF-8 文本文件,可以直接编辑、无需重新编译;配置项内嵌在文件中,也可以通过命令行标志传入。未提供定义文件时,DATA_FILE 默认指向脚本自身(源码 L1474-L1478)。

脚本在入口处就 GOTO :DEFINE_MACROS,先定义好宏再跳转到真正的入口,数据段则夹在跳转语句与 :END_DATA_SEGMENT 标签之间(源码 L1-L5L32-L32),运行时逐行回读自身或外部文件来解析这段数据。

定义文件的结构:Data Segment

数据段由四个带标记的区块组成,标记均为成对的双下划线 token:

__DATA__                       :: 数据段开始
__TABLE__ ... __TABLE:END__   :: 表格配置(SET/IF 语句直接执行)
__COLS__  ... __COLS:END__    :: 列定义:每行一个列头的 SGR 参数
__ROWS__  ... __ROWS:END__    :: 行定义:每行一个行头的 SGR 参数
__DATA:END__                   :: 数据段结束

解析流程见 :READ_DATA_SEGMENT 例程(源码 L553-L587):逐行读取文件,遇到 __COLS__ 进入列收集状态,遇到 __ROWS__ 进入行收集状态,遇到 __TABLE__ 则对行内容做 SET/IF 语句求值。三个特殊 token 赋予矩阵布局极大的表达力:

Token 含义 效果
#NUL# 空参数占位 该列/行不应用任何 SGR 参数,显示终端默认值,但单元格文本照常输出
#SPC# 空白占位 该行/列什么都不显示,用于在表格中制造空隙
#LBL# 行内标签 在表格中间的特定行写入一行文本标签,可带格式,结尾自动追加 SGR RESET

__TABLE__ 区块中可以执行单行 SETIF 语句来配置表格:

  • SET "CELL= gYw ":单元格中重复显示的测试文本(默认值就是 gYw,见 源码 L19);
  • SET "STUBHEAD=SGR":左上角表头文本;
  • SET "ALIGN.CELL=C" / ALIGN.BOXHEAD / ALIGN.STUB / ALIGN.STUBHEAD:单元格与表头的对齐方式(L/C/R);
  • SEPARATOR.* 系列:表格边框字符,支持 UTF-8 制表符(如 ─│┼║╎)与 ASCII 回退,例如脚本默认定义中就有一段条件定义:
IF DEFINED SHOW.UTF8 (SET "SEPARATOR.STUBHEAD_BOXHEAD=│") ELSE (SET "SEPARATOR.STUBHEAD_BOXHEAD=:")
IF DEFINED SHOW.UTF8 (SET "SEPARATOR.STUBHEAD_STUB=─") ELSE (SET "SEPARATOR.STUBHEAD_STUB=-")
IF DEFINED SHOW.UTF8 (SET "SEPARATOR.INTERSECT=┘") ELSE (SET "SEPARATOR.INTERSECT=+")

源码 L94-L96)。:RESOLVE_SEPARATORS 例程(源码 L688-L900)实现了分离符的级联推导:如 SEPARATOR.STUB 未显式定义时,SEPARATOR.STUBHEAD_BOXHEADSEPARATOR.STUB_BODY 会用已定义者补齐,两者都缺省时用空格保证列对齐;垂直与水平分隔线同时存在但未指定 SEPARATOR.INTERSECT 时,脚本会按“默认竖线/默认横线延伸”的规则推断交叉点字符,实在无法确定则回退为空格。separator.def 完整演示了所有分离符组合。

若定义文件中包含 SET "UTF8.REQUIRED=#TRUE#",则运行时校验(:VALIDATE_CONFIGURATION源码 L675-L685)会强制要求 UTF-8 控制台支持,否则报错并提示加 /U 运行。

内嵌默认定义:完整的 SGR 矩阵

脚本内嵌的默认定义(源码 L34-L377)覆盖了一个相当完整的 SGR 参数矩阵,这也是直接运行 ansi-color.cmd 时看到的内容:

列(背景色):第一列 #NUL#(默认背景),随后是 8 色与 256 色“加亮”成对出现的背景:

#NUL# | 40m | 100m | 41m | 101m | 42m | 102m | 43m | 103m |
44m | 104m | 45m | 105m | 46m | 106m | 47m | 107m

行(前景色/属性),按 #LBL# 标签分组,每组下面是 16 个前景参数(30m/90m37m/97m 成对):

  1. 属性区(#NUL# 列无属性):1m 粗体、2m 暗淡、3m 斜体、4m 下划线、5m 慢速闪烁、6m 快速闪烁、7m 反显、8m 隐藏、9m 删除线、21m 双下划线(10m–20m 在标准 SGR 中无效,被注释掉);
  2. Normal30m37m 与加亮 90m97m
  3. Bold or increased intensity, 11;30m1;97m
  4. Faint (decreased intensity), 22;30m2;97m
  5. Italic, 3Underline, 4Slow Blink, 5Rapid Blink, 6Reverse video, 7Conceal, 8Crossed-out, 9Double Underline, 21:同样各 16 个前景组合。

行与 #SPC# 空行交替,形成视觉分组。渲染时每个单元格的合成规则见 :BUILD_TABLE源码 L1070-L1104):普通单元格输出 CSI;<行参数> CSI;<列参数> <CELL> CSI m,即行 SGR、列 SGR、测试文本、再 SGR 重置(RESET=!CSI!m源码 L471-L472);#NUL# 列则只输出行参数与测试文本。ESC 字符在脚本中以不可打印字符硬编码初始化(源码 L386-L390),并据此派生出 CSICUU/CUD/CUF/CUB(光标移动)与 CPL(Cursor Previous Line,被进度动画用于“回一行”)等序列(源码 L458-L472)。

附带的定义文件:复刻同类工具的输出

目录中随脚本附带了多个 .def 定义文件,用于复刻其他工具的颜色表输出或演示特定技巧,均使用与内嵌定义相同的结构:

定义文件 用途
colortool.def 复刻 ColorTool.exe -c 的 17 行(默认/粗体/8 色×普通加粗)× 9 列背景输出
ansi-colortool.def 同上,但加上了 UTF-8 ANSI 表格线(║─│┬╫
ubuntu.def 复刻 Ubuntu 社区经典的“全部终端颜色”脚本输出
sgr.defsgr-intensity.def 更完整的 SGR 参数/强度矩阵
tsgr.defattrib.def 文本属性与 SGR 组合测试
fgbg.defcolortest.defcrisman.defrosetta.def 前景×背景笛卡尔积等变体(crisman.def 致敬了脚本注释中引用的 Daniel Crisman 的 BASH 脚本,见 源码 L1532-L1561
rainbow.defplaid.def 256 色调色板的彩虹/格纹展示
lbl.defseparator.def 专门演示 #LBL# 标签与全部分离符配置

指定其中任意一个即可渲染对应矩阵,例如:ansi-color.cmd ubuntu.def

诊断模式:/A 与 /R

文档特别强调了两个对编写配色方案有用的诊断模式:

/A——把转义序列变成可见文本。开启后,输出中的真实 ESC 字符会被替换为其 Unicode 码位表示 ␛(源码 L454-L456)。配合重定向:

ansi-color /a > out.txt

会得到一个纯文本文件,完整展示“本应生成”的控制字符序列。这在排查“某处渲染不对劲”时非常直接——你比对的是序列本身而不是肉眼观察的色块。该模式强制要求 UTF-8(实现上同时隐含 /U),并禁用进度动画以保证输出干净。

/R——R1C1 行列引用寻址。借用 Excel 的 R1C1 引用方案,把单元格文本替换为 R<行号>C<列号> 标识。此时生成的表格仍保持行列布局,你只需记下某个异常色块所在的 RxCy 引用,就能反查回定义文件中对应的行 SGR 参数与列 SGR 参数组合,快速定位是哪一个属性与颜色组合出了问题。注意 #SPC# 空格列不计入 R1C1 的列号(源码 L1093-L1094)。

源码深读:从定义文件到表格输出的流水线

整个脚本的主流程非常清晰(源码 L522-L545):

PARSE_ARGS → READ_DATA_SEGMENT → VALIDATE_CONFIGURATION
           → RESOLVE_SEPARATORS → BUILD_TABLE → DISPLAY_TABLE
  1. 初始化:确定 ESC/CSI/光标序列常量;若需要 UTF-8,先 chcp 读取当前代码页(注意脚本用了一个小技巧——CHCP 的输出是本地化的,脚本取输出字符串的最后一个值作为代码页,见 源码 L400-L430),记录原值以便退出时恢复;
  2. READ_DATA_SEGMENT:按上文所述解析 __TABLE__/__COLS__/__ROWS__ 三个区块,计算行头/列头的最大宽度(STUB.MAX_WIDTHCOL.MAX_WIDTH);
  3. RESOLVE_SEPARATORS:把分离符的级联规则落成具体字符,并计算垂直分离符的最大宽度、做居中补齐;
  4. BUILD_TABLE:先构造表头行与水平分隔线,再逐行逐列拼接单元格字符串,全部存入 TABLE[#] 缓冲数组——这种“先全部算好再输出”的方式让最终显示瞬间完成,而计算期间屏幕上的转轮动画(@spinner 宏,借助 CPL 光标上移序列原地刷新帧,源码 L1348-L1382)提供进度反馈;
  5. DISPLAY_TABLE:逐行 ECHO 缓冲内容,结束后恢复原代码页并以 0 退出。

值得留意的工程细节是 :DEFINE_MACROS 区块(源码 L1126-L1420):@strlen@maxval@repeat@trim/@ltrim/@rtrim@align@counter@spinner@exit 等宏全部用批处理的双次展开技巧实现——例如 @strlen 用 4096→1 的二分探测求字符串长度,@align 则按 L/C/R 计算前后填充空格。这也是文档所说“脚本本身是 UTF-8 文本、可直接编辑配置”能够成立的基础:你改的是纯文本规则,改完即生效。

在 PowerShell 下批量运行所有定义

文档最后给出了在 PowerShell 中一次性预览目录下所有定义(含子目录)的技巧,便于快速定位某个定义文件的输出:

gci -r .\*.def | %{write ($_ | rvpa -r) && .\ansi-color.cmd $_}

小结

ansi-color 是 Windows Terminal 仓库里一个“小而全”的 ANSI 渲染诊断工具:

  • 零依赖、单文件:纯 Batch 实现,唯一外部依赖是 CHCP;无需编译,脚本即定义文件;
  • 数据驱动__TABLE__/__COLS__/__ROWS__ 定义结构 + #NUL#/#SPC#/#LBL# 特殊 token,可表达属性、颜色、强度任意组合的矩阵;
  • 双诊断模式/A 把转义序列落成可见文本便于落盘比对,/R 用 R1C1 引用反查参数组合;
  • 附带 15 个 .def 示例,复刻了 ColorTool、Ubuntu 社区脚本等同类工具的输出,可直接作为编写自定义定义文件的模板。

如果你在调试终端的 SGR 支持(特别是闪烁、隐藏、双下划线等非主流属性)或编写配色方案时想验证某组序列的实际渲染效果,从 src/tools/ansi-color 直接运行 ansi-color.cmd 就是最快的验证手段。

登录后查看全文
热门项目推荐
相关项目推荐