bat 本机实测:6.7MB JS 开语法高亮要 12.4s,cat 只要 1.2ms;关颜色约慢 24 倍

16 阅读4分钟

上一篇写完 fd 之后预告过,这次实测 sharkdp/bat(读星数约 6.1 万,2026-11-05)。

日常看源码,很多人还在 cat foo.py 或直接丢进编辑器。cat 快、无脑,但没有行号、没有语法高亮、看不见制表符和行尾空格;less 能翻页,高亮要自己配。bat 宣传自己是「带语法高亮和 Git 集成的 cat」。听起来美好,可高亮不是免费的——大文件上到底慢多少、管道场景会不会把装饰全关掉、Debian 上命令到底叫 bat 还是 batcat,我在本机跟 GNU cat 正面比了一轮。

本机实测 + AI 辅助整理。测量时间:2026-11-05(Asia/Shanghai)。每组先预热 1 次,再取中位数(中等文件 7 次、大文件 5 次),输出丢到 /dev/null,文件都在页缓存里,属于「热缓存」成绩。需要语法高亮时显式加 --color=always(因为管道//dev/null 不是 TTY,bat 默认会关颜色,否则测到的只是「关掉高亮的 bat」)。


本机环境

OS:    Debian 13 x86_64,8 核
bat:   0.26.1(GitHub release 二进制,979ba22)
cat:   GNU coreutils 9.7
less:  未安装(后面分页相关坑会用到)

安装又踩了和 fd 那篇一样的 apt 坑。本机当天:

$ sudo apt-get install -y bat
E: Unable to locate package bat

官方 README 写得很清楚:Debian/Ubuntu 用 apt 装时,可执行文件经常叫 batcat(跟另一个叫 bat 的包撞名),需要自己 ln -s /usr/bin/batcat ~/.local/bin/bat 或 alias bat=batcat。本机 apt 源直接找不到包,batcat 命令也不存在,于是改走官方 release:

curl -sL -o bat.tar.gz \
  https://github.com/sharkdp/bat/releases/download/v0.26.1/bat-v0.26.1-x86_64-unknown-linux-gnu.tar.gz
tar xzf bat.tar.gz
mkdir -p ~/.local/bin
cp bat-v0.26.1-x86_64-unknown-linux-gnu/bat ~/.local/bin/bat
# 若你装的是 apt 包,通常是:
#   sudo apt install bat
#   ln -s /usr/bin/batcat ~/.local/bin/bat

macOS 一般是 brew install bat。下文统一写 bat。

测试素材

复用 ripgrep / fd 那套 corpus,并额外拼了两个大文件:

文件大小行数用途
typing.py(Python 标准库)130 KB3819中等源码,看启动与高亮
large_concat.py(多个 .py 拼接)2.96 MB84019大文本吞吐
large_js.js(playwright 两个 bundle 拼接)6.67 MB164119更大、语法更「重」的 JS

实测 1:中等源码——高亮一开,从 0.6ms 到 123ms

文件:typing.py(130KB)。TERM=xterm-256color。

命令中位数相对 cat
cat0.60 ms1×
bat --paging=never --color=never4.05 ms≈6.8×
bat --paging=never --color=always123 ms≈205×
bat --paging=never --color=always --style=plain121 ms≈201×
bat --paging=never --color=always -n129 ms≈215×
bat --paging=never --color=always --style=full122 ms≈203×

结论先说清楚:日常「打开看一眼」的体感,bat 的高亮开销在百毫秒量级,人眼几乎无感;但别拿它跟 cat 比吞吐——cat 这条路径几乎只做读和写,bat 还要做语法解析和 ANSI 着色。关颜色后大约慢 7 倍,已经能接受;真正贵的是 --color=always。

输出体积也能侧面说明高亮在干什么(同一文件 132718 字节):

模式输出字节含 ANSI
cat / bat --color=never132718否
bat --color=always --style=plain620018是
bat --color=always -n726950是
bat --color=always(默认装饰)831010是
bat --color=always -A1573300是

高亮后体积大约变成原来的 4.7~6.3 倍;再加 --show-all 接近 12 倍。终端要画的东西变多了,耗时跟着上去并不意外。

实测 2:2.96MB 拼接源码——关颜色 16ms,开高亮 2.4s

命令中位数相对 cat
cat0.94 ms1×
bat --paging=never --color=never16.3 ms≈17×
bat --paging=never --color=always2400 ms≈2540×
bat --paging=never --color=always --style=plain2359 ms≈2496×
bat --paging=never --color=always -n2395 ms≈2534×

关颜色时 bat 大约慢一个数量级出头;一开高亮直接到 2.4 秒。-n(行号)在关颜色时有明显额外成本(上一组无 --color=always 的测量里,同文件 bat -n 中位数约 48ms,对关颜色的 16ms),但高亮本身已经贵到把行号开销淹没了。

实测 3:6.67MB JS——开高亮 12.4s,这才是标题里的数字

命令中位数相对 cat
cat1.21 ms1×
bat --paging=never --color=never28.4 ms≈24×
bat --paging=never --color=always12.42 s≈10278×

同一台机器、同一份热缓存文件:cat 1.2 毫秒,bat 开语法高亮要 十二秒。这不是黑 bat——它本来就不是为「把几兆 JS 刷到 /dev/null」设计的——而是提醒:别把 bat 当高速 cat 用在脚本流水线里;脚本里需要纯文本透传,用 cat,或至少 bat -pp --color=never。

可复现命令(本机路径请自行替换):

# 预热
cat large_js.js >/dev/null
# 关颜色(接近「当 cat 用」)
for i in 1 2 3 4 5; do
  TIMEFORMAT='%R'; time bat --paging=never --color=never large_js.js >/dev/null
done
# 开高亮(会明显变慢)
for i in 1 2 3 4 5; do
  TIMEFORMAT='%R'; time bat --paging=never --color=always large_js.js >/dev/null
done

实测 4:分页、管道、-n / --style=plain / -A

默认管道会「卸妆」

把 stdout 接到管道时,bat 自动关掉颜色,输出字节数与原文件完全一致:

$ bat typing.py | wc -c
132718          # 和文件本身一样,无 ANSI
$ bat --color=always typing.py | wc -c
831010          # 强制保留高亮

这是特性,不是 bug:避免把 \x1b[38;5;... 灌进 grep/wc/重定向文件。代价是——你在脚本里 bat foo \| something 时,看不到高亮;需要颜色时加 --color=always 或 -f(--force-colorization)。

分页:本机没有 less

bat 默认分页器是 less。本机 which less 直接失败(包都没装)。非 TTY / 管道场景下,bat 多数时候会直接把内容打出来,不硬报错;指定 --pager=less 时也可能静默退化。交互终端里若缺少 less,体验会怪——官方也支持 --pager=builtin(内置 minus)或自己指定 more。

日常建议:

# 只想当「更好看的 cat」,永不进分页
alias bat='bat --paging=never'
# 或单次
bat -pp README.md          # -pp = --style=plain --paging=never

-n / --style=plain / -A 的有意义差异

在小样例上(hello\tworld + 行尾空格 + NUL):

$ printf 'hello\tworld\nline2  \n\0binary?\n' > tabs_sample.txt

$ bat --paging=never tabs_sample.txt
# 制表符、行尾空格、NUL 都「原样」——肉眼几乎看不出问题

$ bat --paging=never -A tabs_sample.txt
hello├─┤world␊
line2··␊
␀binary?␊

-A / --show-all 把 Tab、换行、NUL 变成可见符号,查「明明没字符怎么对不齐」时非常有用。--style=plain(-p)去掉网格/表头等装饰,只留内容(和高亮,若颜色开着);-n 加行号,输出会多出一截前缀(本机 typing.py 关颜色时从 132718 → 151813 字节)。


踩坑清单(都在本机碰到过)

坑 1:Debian/Ubuntu 可能是 batcat,本机 apt 还直接找不到包

$ sudo apt-get install -y bat
E: Unable to locate package bat

$ batcat --version
bash: line 1: batcat: command not found

官方说明:apt 装上之后命令经常是 batcat,因为和另一个 bat 撞名。本机连包都定位不到,最终用的是 GitHub release 二进制。换机器时先 which bat batcat,再决定要不要 ln -s。

坑 2:管道自动关颜色——测速时容易「测错对象」

第一次我把 stdout 接到 /dev/null 就开跑,bat 中位数只有约 2ms(中等文件),心里还想「也就慢一点」。加上 --color=always 之后直接跳到 123ms。原因写在 --help 里:automatic 模式只在交互终端启颜色。对比高亮成本,必须显式 --color=always,否则你测到的是「卸妆版 bat」。

坑 3:默认分页器是 less,系统没装时行为很「软」

$ which less
# 无输出,退出码 1

$ bat --paging=always --pager=less short.py | wc -c
# 不报错,仍可能打出内容(本机管道场景下拿到了文件原文长度)

不要假设「没 less 就会失败」。CI 镜像、精简容器里很常见。需要稳定分页用 --pager=builtin,不需要分页就 --paging=never。

坑 4:未知主题只警告,不失败

$ bat --theme=NoSuchThemeXYZ --color=always short.py >/tmp/out 2>/tmp/err
$ echo $?
0
$ cat /tmp/err
[bat warning]: Unknown theme 'NoSuchThemeXYZ', using default.

主题名写错不会非零退出,脚本里检测失败要抓 stderr 或先 --list-themes。本机主题列表一共 28 个。

坑 5:大 JS/大源码开高亮会到秒级

6.67MB 的 JS bundle,--color=always 中位数 12.42s。不是机器卡死,是语法高亮在干活。预览巨文件、做吞吐脚本,请关颜色或改回 cat。


顺手好用的几个参数

bat -n file.py                 # 行号
bat -A file.py                 # 显示 Tab/换行/NUL
bat -pp file.py                # plain + 不分页(很像彩色 cat)
bat --color=always -f          # 管道里也强制颜色和装饰
bat --list-themes              # 看主题
bat --theme=TwoDark file.rs    # 换主题
bat -l python dump.txt         # 强制按某语言高亮

小结

项本机结果
130KB typing.pycat 0.60ms;bat 关颜色 4.05ms;开高亮 123ms
2.96MB 拼接 pycat 0.94ms;关颜色 16ms;开高亮 2.4s
6.67MB JScat 1.2ms;关颜色 28ms;开高亮 12.4s
管道默认自动关 ANSI,输出字节 = 原文件
-ATab/换行/NUL 变可见符号,查隐形字符有用
坑batcat 包名、测速忘开 --color=always、less 缺失、未知主题只警告、大文件高亮秒级

一句话:bat 的价值在「人看源码」——行号、高亮、露出身形的空白符;它的成本也在高亮。 当 cat 的替身跑脚本、刷几兆产物到管道里,请关颜色或直接用 cat。它和 ripgrep、fd 是同一作者路线的「友好默认」:交互体验加分,默认真相要自己测。

完整测量日志在本机 bat/measurements.txt;整理版以后会放到 Gumroad,先关注专栏「GitHub实测笔记」。
上一篇:fd 本机实测(/usr 找 *.so,find 72ms / fd 46ms)。
下一篇预告:sharkdp/hyperfine——前面几篇都在手写「预热 + 中位数」,下一篇换成作者同门的基准测试工具,看它怎么处理预热、导出和异常值,并回头复测 bat 这组数字。有问题评论区见,看到都会回。