cellshot:唯一能对运行中的 TUI 程序截图的终端工具

2026-06-18

给终端输出截图,听起来很简单对吧?截图、粘贴,完事。

但如果你需要把截图放进技术博客、在 CI 里验证 TUI 界面、或者把终端输出喂给 AI 做结构化解析,普通截图的像素点根本不够用。

这篇文章会实测一个 Rust 写的终端截图工具 cellshot(仓库已更名为 kitlangton/terminal-control),它能对正在运行的 TUI 程序截图,输出 SVG/JSON/PNG 格式,还支持通过会话管理做自动化。我会把安装、常用命令、踩坑、和同类工具怎么选都过一遍。

为什么需要专门的终端截图工具?

普通的终端截图方案各有各的短板:

  • 手动截屏(PrtSc/微信截图等)— 每次分辨率、字体、主题都不一样,放在文章里很难看
  • script 命令回放— 只能录文本,不能生成图片
  • asciinema— 录制功能好,但是生成 SVG/PNG 需要额外步骤,不支持交互式控制
  • freeze/termshot— 只能处理管道输入(ls -la | freeze),没法对正在运行的 TUI 程序(比如 htopranger、各种 CLI 菜单工具)截图

cellshot 的差异点在于它的 PTY-first 架构:工具启动一个伪终端(PTY)来运行目标程序,然后把终端输出解析成结构化帧模型,每个字符的坐标、颜色、样式属性都单独存储。配合命名会话的 start/send/wait/stop 四步生命周期,你可以编写脚本来自动化截图流程。

安装

前提是系统装了 Rust 工具链(rustc ≥ 1.93),直接 cargo 安装:

cargo install cellshot --locked

这条命令会拉取并编译 89 个依赖包,总耗时约 2 分钟。如果你的终端默认 120 秒超时,可以用后台运行的方式:

# 放后台跑,别等它
# 等提示符重新出现后再检查版本
cellshot --version
# → cellshot 0.7.0

如果你不想装 Rust 工具链,或者介意编译耗时,同类工具也有 npm/pip/go 的安装方式(后面竞品对比会提到)。

基础截图:cellshot show

最常用的子命令 show,截取一条命令的输出:

cellshot show --cols 80 --rows 15 -- ls -la

--cols--rows 参数控制虚拟终端的视口大小。-- 后面的内容是要执行的程序和参数。

你也可以用它启动其他程序而不只是一条命令,具体看后面的会话管理部分。

SVG/JSON 输出:cellshot save

想同时拿到 SVG 图片和结构化数据,用 save 子命令:

cellshot save --format svg --format json --out terminal-output \
  --cols 80 --rows 12 -- ls -la ~

这条命令会产生两个文件:

  • terminal-output.svg32.5KB,SVG 矢量图。背景色 #0d1117(GitHub Dark),字体 JetBrains Mono,ANSI 颜色映射完整
  • terminal-output.json310.5KB,17,326 行,逐单元的结构化数据

JSON 输出值得细说。每个单元格(cell)包含:

坐标 (x, y) → 文本字符 → 宽度 → 前景色 → 背景色 → 样式属性

用这个数据你可以对终端输出做精确的 diff 比较、或者直接喂给 AI 做结构化解析。JSON 输出了 17K+ 行的数据量,虽然不是每个场景都需要,但在需要机器处理的场景里,这个精细度是普通截图完全给不到的。

交互式会话管理:核心能力

cellshot 最特别的功能是命名会话管理,分为四个步骤:

启动会话

cellshot start demo --cols 40 --rows 10 -- bash

这会创建一个名为 demo 的持久终端会话。查看会话状态:

cellshot status demo
# demo running
# cwd: /path/to/current
# command: bash
# viewport: 40x10

发送指令

⚠️ 这里有一个要留意的点send 子命令不支持裸文本,必须用类型前缀:

# ❌ 这样会报错
cellshot send demo 'echo "hello"'
# Error: unsupported input event...

# ✅ 正确写法
cellshot send demo 'text:echo "=== CELLSHOT TEST ==='
cellshot send demo 'enter'
cellshot send demo 'text:ls -la /tmp'
cellshot send demo 'enter'

支持的输入类型包括:text:<value>(普通文本)、enterctrl-actrl-z、方向键、escapetabbackspace 等。

等待指定文本(CI 自动化核心)

这是把会话管理串进自动化流程的关键命令。wait 会阻塞直到终端中出现指定文本:

cellshot send demo 'text:echo "===READY==="'
cellshot send demo 'enter'
cellshot wait demo '===READY===' --timeout 5

超时参数 --timeout 5 防止无限等待。配合 send 使用,可以在脚本里控制截图时机:

# 一套完整的自动化截图流程
cellshot start ci-session --cols 80 --rows 24 -- bash
cellshot send ci-session 'text:./run-tests.sh'
cellshot send ci-session 'enter'
cellshot send ci-session 'text:echo "===DONE==="'
cellshot send ci-session 'enter'
cellshot wait ci-session '===DONE===' --timeout 30
cellshot save --format png --format json --out ci-result
cellshot stop ci-session

其他常用操作

# 查看会话当前屏幕内容
cellshot show demo

# 调整视口大小(实时生效)
cellshot resize demo --cols 60 --rows 15

# 查看会话日志
cellshot logs demo

# 停止会话
cellshot stop demo

踩坑汇总

实测中碰到几个具体问题:

send 指令必须有类型前缀

上面已经提到了。send 不支持裸文本字符串,每条输入都需要用 text:ctrl-x 指定类型。这不算 bug,但初次使用时很容易忘记。

-- 分隔符不能漏

cellshot 子命令的参数和传给目标程序的参数之间必须用 -- 分隔:

# 正确
cellshot show --cols 80 -- my-app

# 错误:my-app 被 cellshot 当成自己的参数
cellshot show my-app --cols 80

-- 之前是 cellshot 的参数,之后是传给目标程序的参数。

视口设置在启动时决定

命名会话的 viewport 在 start 时指定(--cols/--rows),save 时不能重新设置。如果截完后发现尺寸不对,需要先 resize 再重新 save。不过一次性命令 show/save 可以直接在命令后加 --cols/--rows,不受这个限制。

SVG 字体依赖

生成的 SVG 默认使用 JetBrains Mono 字体。系统如果没有安装这套字体,浏览器会自动 fallback 到系统等宽字体,渲染效果在不同系统上可能有细微差异。

仓库改名

原仓库 kitlangton/cellshot 已更名为 kitlangton/terminal-control,CLI 命令也在逐步迁移到 termctrl。crates.io 上两个包同时存在但内容不同。本文使用的是 cellshot v0.7.0。

同类工具怎么选

维度 cellshot freeze termshot asciinema
核心定位 TUI 截图+自动化 输出转 SVG 输出转图片 录制+回放
交互式截图 ✅ start/send/wait
TUI 应用支持 ✅ 完整 PTY ❌ 仅文本 ❌ 仅管道 部分支持
输出格式 SVG,PNG,TXT,JSON,ANSI SVG,PNG PNG,SVG JSON,SVG
结构化数据 ✅ JSON(逐单元格) JSON(时间戳流)
CI 自动化 ✅ 原生 wait 命令 有限 有限 需额外脚本
安装方式 cargo install npm/pip go install pip

按场景选:

  • 对 TUI 应用做截图或自动化测试 → cellshot,它是目前唯一能处理运行中 TUI 程序截图的选择
  • 日常命令输出转好看的 SVG → freeze,渲染效果更精美,社区更大
  • 录制终端操作过程分享 → asciinema,生态最成熟
  • 终端录制转 GIF/视频 → terminalizer

总结

cellshot 的差异化在于:它不只是一个截图工具,而是一个终端会话的 结构化采集框架。别的工具把终端输出转成一张图,它把输出拆成了每一个字符的位置、颜色、样式——这种数据精细度让它在以下场景里很有价值:

  • 技术写作:生成风格统一的终端截图放进文章或 README
  • TUI 测试:在 CI 里启动应用→发送命令→等待输出→截图→对比 JSON diff
  • AI 数据采集:把结构化终端输出喂给模型做训练或推理

当然它也不是完美的。社区很小(~76 星),文档示例不多,recording/video 功能依赖 FFmpeg。如果你只是偶尔截个终端图放博客,freeze 可能更省事。但如果你需要在自动化流程里处理 TUI 界面,cellshot 是这个细分领域唯一的选择。