使用 VHS 编写.tape脚本:为 gws(Google Workspace CLI)制作终端演示 GIF/MP4 的完整指南
【免费下载链接】cliGoogle Workspace CLI — one command-line tool for Drive, Gmail, Calendar, Sheets, Docs, Chat, Admin, and more. Dynamically built from Google Discovery Service. Includes AI agent skills.项目地址: https://gitcode.com/gh_mirrors/cli413/cli
VHS 是 Charmbracelet 出品的终端录屏工具,它通过读取.tape脚本文件把终端会话录制为 GIF、MP4 或 WebM。本指南以 gws(Google Workspace CLI)仓库的演示录制实践为核心,讲解.tape文件的语法规则、引号陷阱、录制参数与常见命令,并结合 docs/demo.tape 这一真实工程示例,让读者既能学会编写可复现的终端演示脚本,也能掌握本仓库"场景化 + 隐藏 setup"的录屏编排方法论。
为什么用脚本录制终端演示
终端演示(Demo)是开源项目 README、发布公告和功能宣传中最直观的表达方式,但手工录屏存在几个痛点:手速不稳定、容易打错字、难以重录、无法版本化。VHS 用.tape文本脚本解决了这些问题——录制内容是一份可提交进 Git 仓库的普通文本文件,任何改动都能走 code review,任何机器都能重放。
在 gws 仓库中,演示录制是开发流程的一等公民:
- AGENTS.md 明确说明"Demo recordings are generated with VHS (
.tapefiles)",并给出运行命令vhs docs/demo.tape; - 仓库根目录存放着最终产物 demo.gif,由 docs/demo.tape 生成;
- 仓库提供 npm 包(
@googleworkspace/cli),发布流程与录制脚本相互独立,docs/demo.tape只负责展示 CLI 能力,不参与构建。
基本使用方式只需一条命令:
vhs docs/demo.tapeVHS 会按脚本从上到下逐条执行指令,最终在Output指定的路径产出录屏文件。
关键语法规则:指令分隔与引号闭合
.tape脚本最容易出错的地方不是命令本身,而是"一行里同时出现多个指令"时的解析规则。
Type、Sleep、Enter 是同一行上的独立指令
Type、Sleep、Enter是同一行内用空格分隔的独立指令,Type字符串的右引号是它们之间的分界符。最常见的 bug 是忘记闭合Type字符串,导致Sleep/Enter被当成字面文本敲进终端:
# ✅ CORRECT — closing " before Sleep Type "echo hello" Sleep 300ms Enter # ❌ WRONG — Sleep and Enter are typed as literal text Type "echo hello Sleep 300ms Enter检查每一条Type行时,务必确认:只要该行后面还跟着Sleep或Enter,字符串必须先闭合引号。
用Type@<time>覆盖单条命令的打字速度
TypingSpeed设置的是全局打字速度,但有时需要在某一条命令上放慢(例如 JSON 参数想让观众看清)或加快。VHS 支持在Type后紧跟@加时间,中间不能有空格:
Type@80ms '{"pageSize": 2}' Sleep 100ms这条指令以每字符 80ms 的速度敲入{"pageSize": 2}。仓库的 docs/demo.tape 中就有实战用例——在演示自动分页时,用Type@30ms慢速敲入 JSON 参数,制造"观众能看清关键载荷"的节奏:
Type "gws drive files list" Type " --params '" Sleep 50ms Type@30ms '{"pageSize":2,"fields":"nextPageToken,files(id)"}' Sleep 50ms Type "'" Type " --page-all" Type " | jq -r '.files[]?.id'" Enter Sleep 6s引号的三条规则
- 双引号
"..."是Type的标准分隔符; - 单引号
'...'同样可用,当敲入的内容本身含双引号(如 JSON)时优先使用单引号,避免转义地狱; - 反引号用于在双引号字符串内部转义引号:
Type `VAR="value"`。
AGENTS.md 补充了一个重要限制:VHS 不支持双引号Type字符串内的\"转义,强行使用会直接导致解析错误。因此当要敲入含双引号的 JSON 时,正确姿势是整行改用反引号:
Type `gws drive files list --params '{"pageSize":5}'` Enter嵌套引号:把一条命令拆成多行 Type
当构建包含多层嵌套引号的 shell 命令(例如gws的--params要传 JSON、JSON 里又要写含双引号的fields表达式)时,不要试图一行写完,而是拆成多段Type,每段之间用Sleep制造打字节奏:
Type "gws drive files list --params '" Sleep 100ms Type@80ms '{"pageSize": 2, "fields": "nextPageToken,files(id)"}' Sleep 100ms Type "' --page-all" Sleep 300ms EnterPitfall 总结:凡是一行
Type后面还跟着Sleep或Enter,必须先把字符串闭合。逐行审计是编辑.tape文件的基本功。
Settings:文件顶部的录制参数
Settings指令只能出现在文件顶部——任何非设置指令(Output除外)出现之前。唯一的例外是TypingSpeed,它是唯一允许在脚本中途改动的设置。
仓库 docs/demo.tape 顶部的实际配置如下:
Output docs/demo.mp4 Output docs/demo.gif Set Shell "bash" Set FontSize 18 Set Width 800 Set Height 500 Set TypingSpeed 1ms Set Padding 30 Set LineHeight 1.3与技能文档中的标准配置对比,可以观察到两个值得注意的点:
| 设置项 | 技能文档示例 | demo.tape 实际值 | 说明 |
|---|---|---|---|
Shell | bash | bash | 录制使用的 shell |
FontSize | 14 | 18 | 字号越大,终端行数越少,适合竖屏展示 |
Width/Height | 1200/1200 | 800/500 | 横屏宽高比,接近视频封面比例 |
TypingSpeed | 40ms | 1ms | demo 里几乎瞬间敲完,靠Sleep控制节奏 |
Padding | 20 | 30 | 画面内边距 |
WindowBar/Theme | Colorful/Catppuccin Mocha | 未设置(默认) | 主题与窗口栏样式可选配置 |
注意 docs/demo.tape 使用了两条Output指令同时产出 MP4 和 GIF——这是"一次录制、多格式分发"的实用技巧,可适配 README 内嵌与视频平台两种场景。
一个完整的标准设置块长这样:
Output demo.gif Set Shell "bash" Set FontSize 14 Set Width 1200 Set Height 1200 Set Theme "Catppuccin Mocha" Set WindowBar Colorful Set WindowBarSize 40 Set TypingSpeed 40ms Set Padding 20常用命令速查
| 命令 | 示例 | 说明 |
|---|---|---|
Output | Output demo.gif | 输出文件,支持.gif、.mp4、.webm,可多次指定 |
Type | Type "ls -la" | 逐字符敲入文本 |
Type@<time> | Type@80ms "slow" | 覆盖该条指令的打字速度 |
Sleep | Sleep 2s、Sleep 300ms | 暂停录制(等待命令输出或控制节奏) |
Enter | Enter | 回车 |
Hide/Show | Hide...Show | 隐藏/显示录制内容,常用于跳过 setup 命令 |
Ctrl+<key> | Ctrl+C | 组合键 |
Tab、Space、Backspace | Tab 2 | 可带重复次数 |
Up、Down、Left、Right | Up 3 | 方向键,可带次数 |
Wait | Wait /pattern/ | 等待屏幕出现匹配正则的内容 |
Screenshot | Screenshot out.png | 截取当前帧为图片 |
Env | Env FOO "bar" | 设置环境变量 |
Source | Source other.tape | 引入另一个 tape 文件 |
Require | Require jq | 断言外部程序存在,不存在则报错 |
Hide/Show:把 setup 藏起来
录屏时,export PATH、创建测试数据、clear这类准备工作既冗长又不该出现在成片里。Hide之后的指令不进入画面,直到Show恢复录制。技能文档给出的标准模式:
Hide Type "export PATH=$PWD/target/release:$PATH" Enter Type "clear" Enter Sleep 2s Showdocs/demo.tape 把这个模式用到了极致:开头一大段隐藏的 setup 里,先 mock 一个gemini命令保证演示结果确定性,再设置 PATH,然后用真实的gws drive files create在 Google Drive 上创建一个gws-demo文件夹和三个演示文件,最后clear后Show,观众看到的直接是干净的演示起点:
Hide # Mock gemini CLI for deterministic demo Type 'function gemini() { echo "Why do Java developers wear glasses? Because they don'"'"'t C#."; }' Enter Type "export -f gemini" Enter Type "export PATH=$PWD/target/release:$PWD/target/debug:$PATH" Enter Type "set -e" Enter Sleep 1s Type `DEMO=$(gws drive files create --json '{"name":"gws-demo","mimeType":"application/vnd.google-apps.folder"}' | jq -r '.id')` Enter Sleep 3s Type `gws drive files create --json "{\"name\":\"meeting-notes.md\",\"mimeType\":\"text/markdown\",\"parents\":[\"$DEMO\"]}" > /dev/null` Enter Sleep 2s ... Type "clear" Enter Sleep 1s Show这段代码还示范了几个进阶技巧:
- 用
function gemini()mock 外部 AI 命令,让演示输出可预测("deterministic demo"); - 用
DEMO=$(...) | jq -r '.id'把 API 返回值存进 shell 变量,后续命令引用$DEMO; - 每条真实 API 调用后都给了
Sleep 2s~3s,而纯本地操作(clear)只给Sleep 1s甚至500ms——这与技能文档检查清单第 3 条"网络调用可能需要 8s+ 的等待"的原则一致。
场景化编排:ASCII 标题卡与章节节奏
一份好的 demo 不只是命令流水账,而是有起承转合的"剧本"。gws 仓库的做法是用 ASCII art 标题卡切分章节:art/ 目录下存放intro.txt、scene1.txt~scene9.txt、outro.txt等标题卡,scripts/show-art.sh负责"清屏 + 打印":
#!/bin/bash clear cat "$1"每张标题卡都是一幅纯文本场景画,例如 art/scene1.txt:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🤖 WHAT IS GWS? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ A single CLI for ALL Google Workspace APIs. Perfect for: 🤖 AI agents 📜 Shell scripts ⚡ Power users 📊 Automation在 docs/demo.tape 中,每个场景的编排套路是固定的:Hide→Type "./scripts/show-art.sh art/sceneN.txt" Enter→Sleep 1s→Show→Sleep 1s→ 敲入该场景要演示的真实命令 → 等待输出。例如 Scene 4 展示"按文件夹查询文件列表":
Type "gws drive files list" Type ` --params "{\"q\":\"$Q\",\"fields\":\"files(name,mimeType)\"}"` Enter Sleep 3sScene 6 展示 Gmail 发信(base64 编码 raw 消息 +jq美化输出),Scene 8 展示 Sheets 追加数据(--params传spreadsheetId/range/valueInputOption,--json传values),Scene 9 展示自动分页(--page-all)。整个脚本从 intro 到 outro 共 9 个场景,最后隐藏执行清理(删除演示文件夹),形成完整闭环。
该
--page-all自动分页能力对应 gws 的--page-all全局参数,与 crates/google-workspace-cli/src 中的分页处理逻辑配套,录制脚本直接以真实 CLI 行为作为演示内容,保证了"所见即所得"。
编辑 Tape 文件的自检清单
技能文档给出的五条检查项,是每个.tape文件提交前的必备体检:
- 每条
Type字符串必须闭合——在Sleep/Enter出现在同一行之前; - 多行 Type 拼接的 shell 命令——确保最后一行闭合字符串且包含
Enter; - Sleep 时长要够命令执行完——网络调用可能需要 8s+;
- Settings 放在顶部——只有
TypingSpeed可以出现在后面; - 提交前本地测试——运行
vhs <file>.tape完整重放一遍。
对照 docs/demo.tape 可以再加三条仓库级经验:
- 单行命令更可靠:脚本开头注释写着 "Single line commands for reliability"——需要多行拼接时,尽量用
Type分段 + 反引号包裹 JSON,而不是依赖 shell 续行符; - 注释即剧本:用
# ╔═...╗这类分隔注释标注章节边界,让脚本本身可读、可评审; - 善用
> /dev/null:setup 阶段的创建命令输出会干扰画面,重定向掉,只保留正式演示的输出。
何时使用、何时放弃
.tape脚本适合:README 功能演示、PR 行为验证、发布公告视频、需要"可评审 + 可重放"的录屏。但它也有适用边界:高度交互式的 TUI 操作(如 gws 的 setup_tui.rs 这类需要光标定位的界面)录制难度较高;画面比例需要按目标平台规划(横屏适合 README,竖屏适合短视频);录制依赖真实网络时(如本 demo 中的 Drive/Gmail/Calendar API 调用),需要提前准备好凭据与测试数据,并给足Sleep时间。
对于 gws 这样的命令行项目,vhs docs/demo.tape一行命令即可复现整条演示链路:从.tape脚本 → VHS 解析执行 →docs/demo.gif与docs/demo.mp4双格式产物。掌握本文的语法规则与场景编排方法后,你完全可以把这套"脚本化录屏"流程复制到自己的项目中。
【免费下载链接】cliGoogle Workspace CLI — one command-line tool for Drive, Gmail, Calendar, Sheets, Docs, Chat, Admin, and more. Dynamically built from Google Discovery Service. Includes AI agent skills.项目地址: https://gitcode.com/gh_mirrors/cli413/cli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考