飞书官方命令行工具完整上手手册,补充截至 2026-08-16 的最新版本、能力范围与使用建议。
lark-cli 使用文档
这是一篇适合直接收藏的
lark-cli长文手册。 我在原始技术文档基础上,补了截至 2026-08-16 的最新版本信息、官方能力概览和几张速览图,方便公众号阅读。

图 1:GitHub 仓库的 Open Graph 预览图。来源:github.com/larksuite/cli。
先看结论
lark-cli是飞书官方开源 CLI,定位很明确:让人和 AI Agent 都能在终端里操作飞书。截至 2026-08-16,GitHub 官方仓库显示它覆盖 18 个业务域、200+ 命令、26 个 AI Agent Skills。 截至 2026-08-13,最新 release 已经是 v1.0.87,比很多旧文档里提到的 1.0.85更新。如果你只想尽快上手,先看本文里的 图 2 / 图 3 和第二节身份模型,再回头查具体命令。

图 2:飞书官网文章里引用的 lark-cli GitHub README 页面截图。来源:feishu.cn。

图 3:一篇公开教程里的 Skills 安装界面截图,可直观看到 npx skills add larksuite/cli -y -g 的效果。来源:腾讯云开发者社区。
最新动态(截至 2026-08-16)
官方仓库: larksuite/cli最新 release: v1.0.87发布时间: 2026-08-13官方 README 当前描述:18 个业务域、200+ 命令、26 个 AI Agent Skills 推荐安装命令: npx @larksuite/cli@latest install推荐升级命令: lark-cli update
飞书(Lark)开放平台的命令行工具,覆盖云文档、知识库、消息、日历、邮件、表格、多维表格、任务、审批、会议等能力。
版本:本文原稿基于 1.0.78编写;截至2026-08-16,官方最新 release 为v1.0.87可执行文件路径:安装后请用 which lark-cli以本机实际结果为准配置文件: ~/.lark-cli/config.json
文档中所有 token、空间 ID、用户 ID、域名均为占位符,实际使用时替换成自己的值。
零、这是什么
lark-cli 把飞书开放平台的 API 包装成了命令行。原本要写代码调接口、处理 token 刷新、翻分页、轮询异步任务的活儿,现在一条命令就能完成。
它解决的核心问题是把飞书里的数据变成可以用管道处理的文本流。举几个实际例子:
# 我拥有的文档有多少篇、都是什么lark-cli drive +search --mine --page-size 20 --as user \ --jq '.data.results[] | .result_meta.token'# 今天的日程lark-cli calendar +agenda --as user# 分配给我的任务lark-cli task +get-my-tasks --as user# 某篇文档被谁看过lark-cli drive file.view_records list --file-token <token> --file-type docx --as user输出都是 JSON,可以直接接 jq、awk、sort 做统计,也可以写成脚本定时跑。这是它和网页版飞书最本质的区别 —— 网页版适合看单篇,命令行适合批量和聚合。
能做什么
drivedocssheetsbasewiki | ||
immailcalendartaskapproval | ||
vcminutesnote | ||
contactattendanceokr | ||
appswhiteboardslidesmindnotesevent |
适合与不适合
适合:批量处理(几百篇文档的导出、归档、权限梳理)、跨文档聚合统计、定时任务、把飞书数据接进其他工具链。
不适合:需要精细排版的文档编辑(虽然能写,但所见即所得还是网页版顺手)、一次性的简单查看。
上手路径
如果你是第一次用,按这个顺序看最快:
先读第二节身份模型。这是最容易出错的地方, --as用错会拿到空结果而且不报错。再读第一节命令模型,学会用 --help和schema自己查命令,不用记。然后读第四节输出与解析,尤其是 ok字段的判断规则。需要动写操作前,读第五节安全门禁。 剩下的按需查阅。第十二节的踩坑记录建议通读一遍,都是实际踩过的。
最省事的自查命令:
lark-cli doctor # 一次性检查配置、认证、连通性lark-cli whoami# 看当前是什么身份在操作一、命令模型
1.1 三层结构
命令按抽象程度分三层,优先用高层的。
lark-cli drive +search | + 前缀。一条命令完成一个完整任务,自动处理 URL 解析、分页、异步任务轮询、多步编排。首选 | |
lark-cli drive file.view_records list | ||
lark-cli api GET /open-apis/... |
快捷命令不只是省字数。以 drive +export 为例,它内部完成「发起导出任务 → 轮询任务状态 → 下载结果文件」三步;docs +media-insert 更是四步编排并带自动回滚。用类型化命令自己拼这些流程容易漏掉轮询和错误处理。
1.2 自我发现
不用记命令,靠这四条把命令面摸清:
lark-cli --help# 列出全部领域lark-cli drive --help# 列出该领域的快捷命令 + API 资源lark-cli drive +search --help# 单个命令的全部参数、风险等级、使用提示lark-cli schema drive.metas.batch_query # API 的入参、出参、所需权限、文档链接schema 的输出里有几个关键字段:
inputSchema每个参数对应的flag名、类型、枚举值、默认值、上下限outputSchema返回结构,写--jq表达式前先看这里_meta.scopes所需权限_meta.risk风险等级_meta.doc_url官方 API Explorer 链接
命令的 --help 底部常有 Tips 段落,写了参数互斥关系和易错点,值得一读。
1.3 参数形式
领域命令(drive、docs、wiki、im 等)的参数一律走标志,没有位置参数:
# 错误:会报 positional arguments are not supportedlark-cli drive +inspect "https://example.feishu.cn/docx/<token>"# 正确lark-cli drive +inspect --url "https://example.feishu.cn/docx/<token>"只有三个工具类命令接受位置参数:api <METHOD> <PATH>、schema <service.resource.method>、skills read <name>。
二、身份模型
这是最容易出错的地方,务必先理解。
2.1 两种身份
--as user | auth login 授权,两层都要满足 | ||
--as bot |
每次命令输出里的 identity 字段表示当前生效身份。
2.2 为什么必须显式指定
bot 和 user 的行为差异很大,用错身份不会报错,只会返回空结果或错数据:
bot 看不到用户的个人资源。 用 --as bot查日程,返回的是 bot 自己那个空日历,不是你的日程。查云空间同理。bot 无法代表用户操作。 用 bot 发消息是以应用名义发出,用 bot 创建文档归属于 bot 而不是你。
所以凡是查「我的」东西,一律加 --as user。本文所有示例都显式带上,避免依赖默认值。
2.3 查看当前身份
lark-cli whoami# 最快,看实际生效的身份lark-cli auth status --json --verify # 详细:登录态、token 有效期、已授权 scope 全表whoami 的输出:
{"profile":"cli_xxxxxxxxxxxx","appId":"cli_xxxxxxxxxxxx","brand":"feishu","defaultAs":"auto","identity":"user","identitySource":"auto_detect","available":true,"tokenStatus":"needs_refresh","onBehalfOf":{"userName":"<你的名字>","openId":"ou_xxxxxxxx"}}tokenStatus 是 needs_refresh 不影响使用,下次调用会自动刷新。但 auth status 里的 refreshExpiresAt 一旦过期,就必须重新 login。
三、认证与授权
3.1 首次配置
lark-cli config init --new # 会阻塞,等待你打开链接完成应用配置命令输出里的 verification_url 可以转成二维码方便手机扫:
lark-cli auth qrcode <url> --output ./qr.png # PNGlark-cli auth qrcode <url> --ascii # 终端里直接显示URL 要当作不可修改的字符串,别做 URL 编码、别重新拼 query,否则会失效。
3.2 授权
auth login必须指定范围,三种方式可叠加:
lark-cli auth login --domain all # 全部权限lark-cli auth login --domain docs --domain drive # 按业务域,可重复或逗号分隔lark-cli auth login --scope "drive:file:view_record:readonly"# 按具体 scope,最小权限,推荐lark-cli auth login --recommend # 只申请推荐的自动批准 scopelark-cli auth login --scope "..." --exclude drive:file:download # 排除某些 scope多次 login 的 scope 会累积(增量授权),不会互相覆盖。
3.3 权限不足怎么办
错误信封会直接告诉你缺什么以及怎么修:
{"ok":false,"identity":"user","error":{"type":"authorization","subtype":"missing_scope","message":"missing required scope(s): contact:user.basic_profile:readonly","hint":"run `lark-cli auth login --scope \"contact:user.basic_profile:readonly\"` ...","missing_scopes":["contact:user.basic_profile:readonly"]}}照 hint 执行即可。注意区分身份:
user 身份缺权限 → 跑 auth login --scope "<缺失的 scope>"bot 身份缺权限 → 不要执行 auth login,bot 没有 login 流程。要去开发者后台开通,错误里的console_url就是入口
3.4 权限自查
lark-cli auth check --scope "drive:file:view_record:readonly"# 多个用空格分隔lark-cli auth scopes # 应用侧已开通的全部权限auth check 返回 granted 和 missing 两个数组,比在 auth status 那一大串 scope 里肉眼找要快:
{"granted":["drive:file:view_record:readonly"],"missing":null,"ok":true}3.5 退出与撤销
lark-cli auth logout# 只清本机登录态,返回 loggedOut:trueauth logout不会撤销服务端的授权,也无法单独撤销某一个 scope。要彻底取消授权,得由用户到飞书的授权管理页面操作。
3.6 多账号
lark-cli profile list # 列出全部 profilelark-cli profile add <name> # 新增lark-cli profile use <name> # 切换,用 - 可以切回上一个lark-cli profile rename <old> <new>lark-cli profile remove <name>一个 profile 对应一套 appId 加登录态,多租户或多应用场景用它隔离。
四、输出与解析
4.1 JSON 信封
成功和失败的结构不同,且走不同的流:
成功 → stdout,退出码 0:
{"ok":true,"identity":"user","data":{ ... },"meta":{"count":1}}失败 → stderr,退出码非 0:
{"ok":false,"identity":"user","error":{"type":"...","subtype":"...","code":1069604,"message":"...","hint":"..."}}判断成功必须用 ok == true 或进程退出码,不要用 code == 0。 成功信封里根本没有顶层 code 字段,code 只出现在错误信封的 error 内部,含义是上游 OpenAPI 的数字错误码。如果按飞书 OpenAPI 老格式 {"code":0,"msg":"ok"} 去判断,所有成功调用都会被误判成失败。写入类操作里这个误判很危险,可能绕过幂等逻辑造成重复创建。
因为错误走 stderr,脚本里写 2>/dev/null 会把错误信息一起丢掉。调试阶段建议保留 stderr。
4.2 去掉 _notice 噪音
每次调用返回里都可能带 _notice,提示新版本或技能版本脱节,会干扰 jq 解析和阅读。两个环境变量可以关掉:
LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1 lark-cli whoami脚本里建议直接 export 一次。或者用 jq 'del(._notice)' 事后删掉。
4.3 过滤与格式
--jq '<表达式>'# 内置 jq 过滤--format json|pretty|table|ndjson|csv--json # --format json 的简写内置 --jq不支持 --arg,只接受表达式本身。需要注入外部变量时改用系统 jq:
lark-cli drive file.view_records list --file-token "$tk" --file-type docx --as user | \ jq -r --arg n "$title"'.data.items[]? | [$n, .name, .last_view_time] | @tsv'写 --jq 之前先用 schema 看 outputSchema,或者先跑一次不带过滤的命令看真实结构。不同接口的返回字段名不统一(详见第九节)。
4.4 分页
--page-size N # 每页条数,各接口上限不同,常见 50 / 200--page-all # 自动翻页--page-limit N # 配合 --page-all 的最大页数,默认 10,0 表示不限--page-token TOKEN # 手动指定起始页,传了它就只取单页大数据量务必配 --page-limit,默认只翻 10 页容易以为数据不全,设成 0 又可能跑很久。
五、安全与风险门禁
5.1 风险三级
每条命令的 --help 顶部会标注,schema 输出里对应 _meta.risk:
read | ||
write | ||
high-risk-write | 必须加 --yes |
5.2 exit 10 确认门禁
高危命令不带 --yes 时,CLI 会以退出码 10 拒绝执行,并返回结构化提示:
{"ok":false,"identity":"user","error":{"type":"confirmation","subtype":"confirmation_required","message":"drive +delete requires confirmation","hint":"add --yes to confirm","risk":"high-risk-write","action":"drive +delete"}}这不是普通报错,是设计好的确认环节。正确处理方式:把 action、risk 和关键参数摆给使用者看,明确说明这是高危操作,得到同意后再在原命令末尾追加 --yes 重试。被拒绝就终止,不要改参数绕过。
自动化脚本里尤其不要「见到 exit 10 就自动补 --yes 重试」,那等于把这道门禁彻底废掉。
5.3 用 dry-run 预览
lark-cli drive +delete --file-token <token> --type docx --dry-run --as user--dry-run不触发确认门禁,退出码 0,会打印完整的请求详情(method、url、params、body)。危险操作执行前先用它看一眼实际会发什么请求。
5.4 路径限制
--file、--output、--output-dir、@file 这类路径参数只接受当前目录下的相对路径,传绝对路径会被拒:
unsafe output path: --output must be a relative path within the current directory,got "/tmp/abs_test.bin" (hint: use a relative path like ./filename; ...)所以下载前要先 cd 到目标目录,再用 --output ./name.ext。需要读取工作目录之外的文件时,支持 stdin 的参数可以用 - 代替路径。大 JSON 体也建议走 stdin,省去转义麻烦。
5.5 其他
不要把 appSecret、accessToken 打印到终端。 config show已自动把 secret 显示为****。写入和删除前确认意图,这一条比什么门禁都重要。
六、云空间与云文档(drive)
6.1 先拿 token
绝大多数命令的入口参数是 token。URL 路径本身就能看出 token 类型:
/docx/<token>/doc/<token>、/sheets/<token> | file_token 用 |
/drive/folder/<token> | folder_token |
/wiki/<token> | file_token,必须解包 |
/page/<token> | file_type=apps |
知识库链接必须先解包:
lark-cli drive +inspect --url "https://example.feishu.cn/wiki/<node_token>" --as user返回 type、title、token、url;wiki 场景会额外返回 wiki_node 子对象,含 space_id、node_token、obj_token、obj_type。传裸 token 而非完整 URL 时需要额外指定 --type。
什么时候该用 +inspect:URL 是 wiki 链接、或者 token 来源不明、或者需要顺带拿到标题和规范化 URL。如果 URL 路径已经明确是 /docx/、/sheets/ 这类,token 可以直接用,不必把 +inspect 当固定前置步骤。
文件类型枚举:doc(旧版文档)、docx(新版文档)、sheet、bitable、file、folder、wiki、mindnote、minutes、slides。+search --doc-types 另外还支持 catalog 和 shortcut。
注意 bitable 是多维表格的内部代号,产品名叫 Base。参数里一律填 bitable,填 base 会失败。
6.2 搜索
最省事的入口,比遍历目录快得多:
# 我拥有的全部文档lark-cli drive +search --mine --page-size 20 --as user \ --jq '.data.results[] | [.title_highlighted, .result_meta.token] | @tsv'# 关键词 + 类型 + 时间窗lark-cli drive +search --query "评测" --doc-types docx,sheet --edited-since 7d --as user# 限定在某个知识库空间内搜lark-cli drive +search --mine --space-ids <space_id> --as user要点:
--mine是「我拥有的」(服务端按 owner 语义匹配),--created-by-me是「我创建的」(original creator 语义)。文档转手后两者结果不同。注意底层字段名叫creator_ids,但实际匹配的是 owner,名字有误导性。--mine与--creator-ids互斥。返回结构是 .data.results[],元数据在.result_meta子对象下,不是.data.items[]。这与其他接口不一致,是最常见的踩坑点。title_highlighted含高亮标签,实测形如<h>评测</h>脚本和代码。当标题用(尤其是拿来做文件名)必须先剥掉<h>、<hb>标签。--page-size默认 15、最大 20,比其他接口小得多。传超限值不会报错,而是静默截断成 20(实测传 25,返回的page_token里size仍为 20)。想拿更多结果只能翻页。--query上限 30 个 Unicode 码点(中文按 1 个算),超了服务端直接报99992402 field validation failed,是报错而非截断。时间参数接受相对值( 7d、1m、1y)、绝对日期(2026-04-01)、RFC3339、Unix 秒。注意1m是固定 30 天,不是日历月。my_edit_time、my_comment_time在服务端按小时聚合,传入亚小时精度会被 snap 并在 stderr 给出提示。--folder-tokens限定为只搜云空间文档,--space-ids限定为只搜知识库,两者互斥不能同时用。返回的 total官方确认不准。 要统计数量得按实际results去重累加,别拿total当结论。
6.3 列目录
lark-cli drive files list --page-size 200 --order-by EditedTime --as user # 根目录lark-cli drive files list --folder-token <folder_token> --page-size 200 --as user # 指定文件夹要点:
不填 --folder-token时返回云空间根目录清单,此时不支持分页,也不返回快捷方式。不要拿根目录的返回条数去推断文档总量。--page-size上限 200,建议直接给 200。--order-by可选EditedTime或CreatedTime,配--direction ASC|DESC。这是原生命令,不递归。要遍历文件夹树得自己对 type=folder的条目下钻,每个目录维护自己的page_token,按has_more=false收尾。别把 --page-all的输出直接喂给 JSON 解析器(多页会拼成多个 JSON 对象),也别2>&1之后再解析。
按关键词、时间、归属找文档用 +search;只有在需要完整目录树结构时才用 files list 递归。
6.4 上传下载与导入导出
lark-cli drive +upload --help# 上传本地文件lark-cli drive +download --help# 下载文件lark-cli drive +export --help# 云文档导出为本地文件(docx/sheet/bitable/slides/wiki)lark-cli drive +import --help# 本地文件导入为云文档lark-cli drive +export-download --help# 按 file_token 下载已导出的文件lark-cli drive +task_result --help# 轮询导入/导出/移动/删除等异步任务导入导出都是异步任务。+export 内部固定最多轮询 10 次、间隔 5 秒;超时返回 ticket 和 timed_out=true 并不代表失败,拿 ticket 继续查即可:
lark-cli drive +task_result --scenario export --ticket <ticket> --file-token <token> --as user导出格式有硬限制:
markdown | |
csv | --sub-id 指定子表 |
base | |
--only-schema |
导出错误码:1069914 token 与 type 不匹配、1069902 无权限、99991679 缺 scope。
导入的注意事项:并发导入同一位置会冲突,报 232140101 / 232140100 / 233523001,必须串行执行,失败重试不超过 3 次。PPTX 单文件上限 500MB。
另外两个容易绕远路的场景:复制在线文档直接用 drive files copy,不要 +export 再 +import;本地 Excel / CSV 导入成多维表格,第一步是 drive +import --type bitable,不是切到 base 域。改标题用 drive files patch 的 new_title。
6.5 本地与云端同步
四个命令方向不同,选错会丢数据,务必看清:
+status | new_local / new_remote / modified / unchanged 四类差异,不改动任何东西 | |
+pull | ||
+push | ||
+sync |
共同限制:四者都只处理 type=file 的二进制文件,会跳过在线文档(docx、sheet 等)和快捷方式。--local-dir 必须是当前目录下的相对路径,软链接也会被拒。
各自的默认行为差异很大,是数据安全的关键:
+status默认detection=exact,会下载远端字节流算 SHA-256,流量约等于双端共有文件的总大小。只想快速看差异就加--quick,只比修改时间。+pull --if-exists默认overwrite。重复增量同步推荐改成smart,也可以用skip。+push --if-exists默认是skip而非overwrite。原因是overwrite依赖灰度中的version字段,未开通该能力的租户上会导致整批失败。重名处理: +pull --on-duplicate-remote支持fail(默认)/rename/newest/oldest;+push只有fail/newest/oldest,没有rename。+sync的冲突项按--on-conflict=remote-wins|local-wins|keep-both|ask处理。
危险参数:+pull --delete-local 和 +push --delete-remote 都是 high-risk-write,必须配 --yes,否则在校验阶段就被拒。--delete-remote --yes 还会预检 space:document:delete 权限。有一个传输条目失败时,整个删除阶段会被跳过(这是保护机制)。
动手前先跑 +status --quick 看差异,确认无误再执行。也可以更细粒度地处理:对 new_remote 单独 +download,对 new_local 单独 +upload。
6.6 权限与协作者
lark-cli drive +member-add --help# 添加协作者lark-cli drive +apply-permission --help# 向文档所有者申请权限lark-cli drive permission.public get --help# 查看公开链接设置lark-cli drive permission.members auth --help# 校验某人是否具备某权限lark-cli drive permission.members transfer_owner --help# 转移所有者+member-add 的枚举值(填错会直接报参数校验失败):
--member-type | emailopenid / unionid / openchat / opendepartmentid / userid / groupid / wikispaceid / appid |
--perm | viewedit / full_access |
--perm-type | containersingle_page |
一次最多传 10 个 --member-id,它们共享同一组 --member-type、--perm、--perm-type。真实写入需要 --yes。用 --member-type=wikispaceid 时必须额外给 --member-kind。
公开链接设置 permission.public patch 的枚举:
link_share_entity:tenant_readable/tenant_editable/anyone_readable/anyone_editable/closedshare_entity:anyone/same_tenant/only_full_accesssecurity_entity、comment_entity:anyone_can_view/anyone_can_edit(前者另有only_full_access)
加人之前先把 ID 解析出来,别靠试错反推类型:用户用 contact +search-user 取 open_id,群用 im +chat-search 取 chat_id,部门用 api POST /open-apis/contact/v3/departments/search 取 open_department_id。
公开权限相关的错误码要单独识别,不要归类成缺 scope:
91009 | |
91010 | |
9101191012 |
transfer_owner 不可逆,执行前务必确认。另外「部门 + bot 身份」是已知不支持的组合,--member-type=opendepartmentid 必须配 --as user。
6.7 评论
lark-cli drive +list-comments --help# 列出评论,支持 URL 解析和 wiki 解包lark-cli drive +add-comment --help# 添加评论6.8 版本历史
lark-cli drive +version-history --help# 列出历史版本lark-cli drive +version-get --help# 下载某个版本lark-cli drive +version-revert --help# 回滚到某个版本lark-cli drive +version-delete --help# 删除某个历史版本(高危)七、知识库(wiki)
7.1 token 体系
知识库是最容易搞混 token 的地方,三种 token 各有用途:
space_id | +node-list | |
node_token | ||
obj_token |
同一个节点的 node_token 和 obj_token 不同,别混用。 向下遍历用 node_token,一切文档级操作用 obj_token。
知识库文档的链接形如 /wiki/<node_token>,直接拿这个 token 去调文档接口会失败,得先用 drive +inspect --url 解包成 obj_token。
7.2 常用命令
lark-cli wiki +space-list --page-all --as user # 列出可访问的空间,拿数字 space_idlark-cli wiki +node-list --space-id <space_id> --page-all --as user # 列出空间根节点lark-cli wiki +node-list --space-id <space_id> --parent-node-token <node_token> --as user # 下钻lark-cli wiki +node-get --help# 按 node_token / obj_token / URL 查节点详情lark-cli wiki +node-create --help# 创建节点lark-cli wiki +move --help# 移动节点,或把云空间文档移入知识库lark-cli wiki +move-to-drive --help# 把知识库节点移出到云空间lark-cli wiki +member-list --help# 空间成员lark-cli wiki +member-add --help# 加空间成员,--member-role 只有 admin / member要点:
知识库操作一律显式加 --as user。--as默认值是auto,实测常被解析成bot,而 bot 看不到你的知识库。my_library配 bot 身份会直接被拒。+node-list默认只取一页,--page-size上限 50,超限会直接报--page-size must be between 1 and 50(与+search的静默截断不同,这里是硬校验)。--page-all还受--page-limit(默认 10)约束,深目录务必把--page-limit设成 0。+member-list同样默认只返回一页。空间成员角色只有 admin和member两种,且不支持一步切换角色,要先+member-remove再+member-add。节点移出知识库到云空间用 wiki +move-to-drive,不是wiki +move也不是drive +move。「我的文档库 / 个人知识库 / my_library」属于知识库的个人库,不等于云空间根目录,两者是不同的存储位置。 删除知识空间前必须先解析出真实 space_id,且无论匹配到几个都要把候选列给使用者确认,不能因为「只命中一条」就自动删。
上传文件到知识库节点下仍然走 drive 域:drive +upload --wiki-token <node_token>。
7.3 遍历整棵树
+node-list不递归,只返回一层。要遍历整棵树,得靠返回里的 has_child 字段自己递归下钻:
walk() {local parent="$1" depth="$2" (( depth > 8 )) && return# 加个深度上限防意外local args=(wiki +node-list --space-id "$SID" --page-all --page-limit 0 --as user) [[ -n "$parent" ]] && args+=(--parent-node-token "$parent") lark-cli "${args[@]}" \ --jq '.data.nodes[]? | [.node_token, .obj_token, .obj_type, .has_child, .title] | @tsv' \ 2>/dev/null | grep -v '^Found' | \while IFS=$'\t'read -r ntoken otoken otype haschild title; doprintf'%s\t%s\t%s\n'"$otoken""$otype""$title" >> "$OUT" [[ "$haschild" == "true" ]] && walk "$ntoken" $((depth + 1))done}walk "" 0但先想清楚要不要这么做。 大知识库动辄上千节点,递归遍历再逐个查元数据要跑几分钟。如果目标只是「找我在这个空间里的文档」,一条 drive +search --mine --space-ids <space_id> 就够了,快得多。递归遍历只在需要完整目录树结构时才值得。
--space-id my_library 是个人文档库的别名,只能配合 --as user。
八、文档内容读写(docs)
本节的所有命令和输出都实测跑过。开始前先明确一点:官方在 docs --help 里要求写 --content 之前必须先读内置指南,因为 XML 语法有不少硬规则:
lark-cli skills read lark-doc # 总览与决策树lark-cli skills read lark-doc references/lark-doc-xml.md # XML 语法(写入前必读)lark-cli skills read lark-doc references/lark-doc-fetch.md # 读取策略8.1 读取:先选范围,再选详细度
+fetch 有两个正交的维度,组合使用。
--scope 决定读多少:
full | ||
outline | --max-depth | |
section | --start-block-id | |
range | --start-block-id--end-block-id(-1 表示到末尾) | |
keyword | --keyword| 分隔多词,OR 语义 |
--detail 决定每块多细:
simple | |
with-ids | |
full |
原则是局部读优于全量读。 大文档直接读整篇既慢又占空间。
实测示例,以一篇有三个一级标题的文档为例:
D=<文档token># 看目录,先摸结构lark-cli docs +fetch --doc $D --scope outline --max-depth 3 --as user \ --jq '.data.document.content'输出(标题 id 可直接用于下一步):
<fragmentmode="outline"><outline><h1id="doxcnWhJ...">第一节 概述</h1><h1id="doxcnUL7...">第二节 待办</h1><h1id="doxcnWOT...">第三节 代码</h1></outline></fragment># 拿上一步的标题 id 精读某一节lark-cli docs +fetch --doc $D --scope section \ --start-block-id doxcnUL7... --detail with-ids --as user \ --jq '.data.document.content'<fragmentmode="section"requested-start="doxcnUL7..."><h1id="doxcnUL7...">第二节 待办</h1><ul><liid="doxcndbG...">验证 str_replace</li><liid="doxcnEYO...">验证 block_insert_after</li></ul></fragment># 只有关键词时,多词 OR 一次召回lark-cli docs +fetch --doc $D --scope keyword --keyword "部署|发布|上线" --as user# 要给人看的话,markdown 更清爽lark-cli docs +fetch --doc $D --doc-format markdown --as user \ --jq '.data.document.content'--doc-format 三个值:xml(默认,保留结构和 block id)、markdown(纯导出,适合阅读)、im-markdown(发飞书消息用)。
局部读取的输出结构要看懂。 设了 --scope 后内容被 <fragment> 包裹,里面可能出现 <excerpt top-block-id="..." parent-block-path="...">。看到 <excerpt> 就意味着这是节选,不代表你看到了那个块的全貌。 想看全貌就拿 top-block-id 再用 section 或 range 拉一次。
表格默认会瘦身,只返回表头加命中行。要整张表得用 range --start-block-id <table-id> --end-block-id <table-id>。
8.2 写入:六种指令
+update --command 的可选值:
str_replace | --content 留空即删除匹配内容 | --pattern |
append | --content | |
overwrite | --content | |
block_insert_after | --block-id--content | |
block_replace | --block-id--content | |
block_delete | --block-id | |
block_move_after | --block-id--src-block-ids | |
block_copy_insert_after | --block-id--src-block-ids |
实测示例:
# 改错别字,最简单的写法,不需要 block idlark-cli docs +update --doc $D --command str_replace \ --pattern "临时文档" --content "测试文档" --as user# 在某个标题后插一个高亮框lark-cli docs +update --doc $D --command block_insert_after \ --block-id doxcnWhJ... \ --content '<callout emoji="bulb"><p>这段是插入的提示。</p></callout>' --as user# 用 markdown 追加一段(append / overwrite 场景 markdown 很方便)lark-cli docs +update --doc $D --command append --doc-format markdown \ --content '## 附录- 条目一- 条目二' --as user# 删除若干块lark-cli docs +update --doc $D --command block_delete \ --block-id "doxcnEYO...,doxcndbG..." --as user格式怎么选:整段写入(append、overwrite、+create)XML 和 Markdown 都行,用户给的是 .md 就用 Markdown;局部精修(str_replace、block_*)坚持用 XML,因为它能稳定表达块结构和样式。
8.3 新建文档
lark-cli docs +create --as user \ --content '<title>项目周报</title><p>本周进展如下。</p><h1>已完成</h1><ul><li>接口联调</li><li>压测报告</li></ul><pre lang="bash"><code>make deploy</code></pre>'返回 document_id、revision_id、url。
8.4 XML 语法要点
完整规则见 references/lark-doc-xml.md,以下是最容易出错的几条。
标准 HTML 标签(p、h1-h9、ul、ol、li、table、blockquote、pre、code、b、em、a 等)语义不变。扩展标签常用的有:
<title> | |
<checkbox done="true|false"> | |
<callout emoji="bulb"> | |
<grid><column width-ratio="..."> | |
<pre lang="bash"><code>...</code></pre> | |
<cite type="user" user-id="ou_xxx"> | |
<bookmark name="标题" href="..."> |
几条硬规则:
转义只针对文本内容,标签本身禁止转义。 正确: <p>A & B,1 < 2</p>;错误:<p>内容</p>。代码必须放在 <pre>里层的<code>中,不能直接写在<pre>下。列表项必须包在 <ul>或<ol>里,不能裸写<li>。行内样式标签有固定嵌套顺序(外到内): <a>→<b>→<em>→<del>→<u>→<code>→<span>→ 文本,闭合顺序严格反转。只有纯文本人名、没有 open_id 时,先用 contact +search-user --query "名字" --as user反查,再写<cite>。
8.5 两个必须知道的陷阱
陷阱一:写入失败时 ok 仍然是 true。
这是 docs +update 最容易踩的坑。str_replace 没匹配到内容、或者用了已失效的 block id,返回是这样的:
{"ok":true,"data":{"document":{"revision_id":7},"result":"failed","warnings":["degrade_code=1011,msg=Instruction produced no document changes. ..."]}}外层 ok: true,退出码也是 0(实测确认),但 data.result 是 "failed",文档实际没有任何改动。所以判断写入是否真正生效,必须检查 data.result == "success",光看 ok 会以为改成功了。脚本里务必这样写:
res=$(lark-cli docs +update --doc $D --command str_replace \ --pattern "旧文本" --content "新文本" --as user --jq '.data.result')[[ "$res" == "success" ]] || echo"写入未生效,检查 pattern 是否匹配"陷阱二:block id 会失效。
overwrite、block_replace、block_delete 之后,受影响的旧 block id 就不能再用了;插入和复制产生的新块,得重新 fetch --detail with-ids 才能拿到 id。连续做多个写操作时不要复用上一轮的 id,否则就会命中陷阱一那种静默失败。
8.6 其他命令
lark-cli docs +media-insert --help# 插入本地图片/附件,四步编排且失败自动回滚lark-cli docs +media-insert --from-clipboard --doc $D --as user # 剪贴板截图直接插入lark-cli docs +media-download --help# 下载文档里的媒体lark-cli docs +media-preview --help# 预览媒体lark-cli docs +history-list --help# 历史版本lark-cli docs +history-revert --help# 回滚到某个版本lark-cli docs +resource-update --type cover --help# 换封面图lark-cli docs +whiteboard-update --help# 用 mermaid / plantuml / DSL 更新画板截图已在剪贴板时优先用 --from-clipboard,比先存盘再 --file 省一步。
8.7 跨域跳转
docs 只管文档正文。读到内容里有这些标签时,要提取 token 切到对应领域:
<sheet token="..." sheet-id="..."> | sheets | |
<bitable token="..." table-id="..."> | base | |
<vc-transcribe-tab vc-node-id="..."> | note +detail | |
<synced_reference src-token="..."> | docs +fetch 读源文档 |
另外几个容易走弯路的:文档评论走 drive +list-comments / +add-comment;复制文档用 drive files copy,别用 +fetch 加 +create 重建;画板细节看 lark-whiteboard 指南。
九、常用查询实例
9.1 文档被谁看过
lark-cli drive file.view_records list \ --file-token <token> --file-type docx --page-size 50 --as user返回 .data.items[],每项含 name(访问者姓名)、viewer_id、avatar_url、last_view_time(Unix 秒)。--viewer-id-type 可选 open_id(默认)、user_id、union_id。
关键限制:返回的是每个人的最近一次访问时间,不是完整流水。 所以「某人今天有没有看过」判断准确(看过就会刷新成当天),但「同一个人今天看了几次」拿不到。
需要 drive:file:view_record:readonly 和 contact:user.base:readonly 权限,且飞书侧仅付费版本开放访问记录能力。
9.2 只要聚合数字
lark-cli drive file.statistics get --file-token <token> --file-type docx --as user返回 pv、uv、pv_today、uv_today、like_count、like_count_today。判断「今天有没有人看过」,看 uv_today 比逐篇翻访问记录快得多。
9.3 批量查文档归属
一次最多 200 个 token:
lark-cli drive metas batch_query --as user \ --data '{"request_docs":[{"doc_token":"<token>","doc_type":"docx"}],"with_url":false}' \ --jq '.data.metas[]? | [.doc_token, .owner_id, .title, .latest_modify_time] | @tsv'返回里还有 create_time、latest_modify_user、sec_label_name(密级标签)。失败的 token 单独放在 .data.failed_list,错误码含义:
970002 | |
970003 | |
970005 |
注意返回顺序与请求顺序不一致,需要按 doc_token 自己对齐。
十、其他领域速查
10.1 消息与群聊(im)
lark-cli im +chat-list --as user # 我加入的群,默认只返回群聊lark-cli im +chat-list --types p2p,group --as user # 含单聊(仅 user 身份)lark-cli im +chat-search --query "<群名>" --as user # 按群名找 chat_idlark-cli im +chat-messages-list --help# 拉取会话消息,支持时间范围lark-cli im +chat-members-list --help# 群成员,users[] 与 bots[] 分开返回lark-cli im +chat-create --help# 建群lark-cli im +feed-group-list --as user # 消息流分组(标签)10.2 日历(calendar)
lark-cli calendar +agenda --as user # 日程,默认今天lark-cli calendar +search-event --help# 按关键词、时间、参与人搜日程lark-cli calendar +create --help# 建日程并邀请参与人lark-cli calendar +update --help# 改日程,增删参与人lark-cli calendar +rsvp --help# 接受 / 拒绝 / 待定lark-cli calendar +freebusy --help# 查忙闲lark-cli calendar +suggestion --help# 模糊时间范围里智能推荐空档lark-cli calendar +room-find --help# 找可用会议室10.3 任务(task)
lark-cli task +get-my-tasks --as user # 分配给我的任务lark-cli task +get-related-tasks --as user # 与我相关的任务lark-cli task +search --helplark-cli task +create --helplark-cli task +complete --help / +reopen --helplark-cli task +assign --help / +followers --help / +reminder --helplark-cli task +tasklist-create --help飞书任务和审批待办是两回事,审批待办走 approval 域。
10.4 会议与妙记(vc / minutes / note)
lark-cli vc +search --help# 搜会议记录,至少要给一个过滤条件lark-cli vc +detail --help# 会议详情,含 note_id 和 minute_tokenlark-cli vc +recording --help# 按会议 ID 或日程 ID 查 minute_tokenlark-cli minutes +search --help# 搜妙记lark-cli minutes +detail --help# 妙记详情,可选摘要/待办/章节/转写/关键词lark-cli minutes +download --help# 下载音视频lark-cli note +transcript --help# 会议笔记的统一转写,存到文件10.5 表格与多维表格(sheets / base)
lark-cli sheets +cells-get --help# 读区域,含值、公式、样式、评论lark-cli sheets +cells-set --help# 写值 / 公式 / 样式 / 数据校验 / 嵌入图片lark-cli sheets +cells-search --help / +cells-replace --helplark-cli sheets +batch-update --help# 多个写操作打包成一个原子请求,失败回滚lark-cli base +base-create --help# 建多维表格lark-cli base +base-get --helplark-cli base +dashboard-block-create --helplark-cli base +advperm-enable --help# 开启高级权限sheets +cells-clear、+cells-batch-clear 不可逆,属于高危。
10.6 邮件(mail)
lark-cli mail +messages --help# 批量读邮件正文,自动按 20 个一批lark-cli mail +draft-create --help# 新建草稿(不用于回复/转发)lark-cli mail +reply --help / +reply-all --help / +forward --helplark-cli mail +draft-send --help# 发送已有草稿lark-cli mail +lint-html --help# 检查邮件 HTML 兼容性,只读不发lark-cli mail +message-trash --help# 软删除,需要 --yes回复和转发默认只存草稿不发送,要立即发送得加 --confirm-send。这个默认值很友好,能避免误发。读多封邮件用 +messages 而不是循环调 +message。
10.7 通讯录、审批、OKR、考勤
lark-cli contact +get-user --as user # 不传 user_id 就是查自己lark-cli contact +search-user --help# 搜人,需要 user 身份lark-cli approval --help# 审批实例与待办lark-cli okr +cycle-list --help / +cycle-detail --helplark-cli attendance --help# 考勤打卡记录lark-cli apps --help# 妙搭应用开发与托管lark-cli event --help# 实时事件订阅十一、内置技能文档
CLI 自带 27 份领域指南,内容比 --help 详实得多,包含完整工作流、字段说明和可复制示例。
lark-cli skills list # 列出全部技能及适用场景lark-cli skills read lark-drive # 读某一份清单:lark-approval、lark-apps、lark-attendance、lark-base、lark-calendar、lark-contact、lark-doc、lark-drive、lark-event、lark-im、lark-mail、lark-markdown、lark-minutes、lark-note、lark-okr、lark-openapi-explorer、lark-shared、lark-sheets、lark-skill-maker、lark-slides、lark-task、lark-vc、lark-vc-agent、lark-whiteboard、lark-wiki、lark-workflow-meeting-summary、lark-workflow-standup-report。
其中 lark-shared 是跨领域的公共约定(身份、认证、JSON 契约、安全规则),建议第一份就读它。
技能内容随 CLI 版本发布,要用 skills read 获取,不要去翻本地的 SKILL.md 文件,避免版本不一致。lark-cli update 会同时更新二进制和技能。
十二、踩坑记录
以下都是实际使用中验证过的。
JSON 前有干扰行。 部分命令(wiki +space-list、+node-list 等)会在 JSON 之前打印一行 Found N item(s),直接管道给系统 jq 会报解析错误。用内置 --jq,或者加 | grep -v '^Found'。
返回字段名不统一。 多数接口是 .data.items[],但 drive +search 是 .data.results[] 且元数据在 .result_meta 下,wiki +node-list 是 .data.nodes[],wiki +space-list 是 .data.spaces[],drive files list 是 .data.files[],metas batch_query 是 .data.metas[]。写 jq 前先跑一次看结构。
搜索结果的标题带高亮标签。title_highlighted、summary_highlighted 里含 <h> / <hb> 标签,实测形如 <h>评测</h>脚本和代码。直接拿去做文件名或字符串比对都会出错,先 gsub("</?hb?>"; "") 剥掉。
total 字段不可信。drive +search 返回的 total 官方明确说不准,统计数量要按实际 results 去重累加。
--page-size 超限的行为不统一。drive +search 会静默截断(传 25 实际按 20 取,不报错),wiki +node-list 则直接报 --page-size must be between 1 and 50。前者更危险:以为取了 25 条其实只有 20 条,容易误判「数据就这么多」。各接口上限不同,先用 schema 看 maximum 字段确认。
--as 默认是 auto,容易落到 bot。 查个人资源(知识库、云空间、日历、邮箱)时 bot 身份会返回空结果而不报错,非常难察觉。所有涉及「我的」数据的命令都显式写 --as user。
metas batch_query 的返回顺序与请求顺序不一致。 要按 doc_token 自己对齐,不能按下标取。
不能用 +inspect 判断文档是否存在。 实测文档删除后,+inspect 依然正常返回 title 和 token(ok: true),只有 docs +fetch 才会报 3380003 Document page has been deleted。要确认文档还在不在,用 +fetch 或 file.statistics get 试探,别拿 +inspect 当存在性检查。
评论内容不能含尖括号。< 和 > 需转义成 < / >。用 +add-comment 会自动兜底,直接调原生 API 得自己处理。
--jq 不支持 --arg。 要注入变量就改用系统 jq,见 4.3。
错误走 stderr。 脚本里图省事加 2>/dev/null,会把错误信封连带 missing_scopes、hint 一起丢掉,排查时反而更慢。
别用 code == 0 判断成功。 见 4.1,这是会导致重复写入的隐患。
docs +update 写入失败时 ok 仍然是 true。 这条单独拎出来说,因为它和上面那条方向相反、危害更隐蔽。str_replace 没匹配到、或用了失效的 block id,返回是 ok: true 且退出码 0,但 data.result 是 "failed",文档实际没改动。判断写入是否生效必须查 data.result == "success",详见 8.5。
绝对路径被拒。--output 之类只收相对路径,见 5.4。
macOS 自带 bash 是 3.2,不支持 wait -n。 写并发脚本时 while (( $(jobs -rp | wc -l) >= 5 )); do wait -n; done 会直接报 invalid option,导致限流完全失效、所有请求一起冲出去。改用分批 wait:
i=0for x in ...; do query "$x" & i=$((i+1))if (( i % 6 == 0 )); thenwait; fi# 每 6 个等一批donewait管道子 shell 里的后台任务等不到。cat list | while read x; do cmd & done; wait 这种写法,while 跑在子 shell 中,外层 wait 等不到里面的后台任务,于是统计跑在任务完成之前,拿到的是中间结果。改成 while ... done < file 的重定向形式。
并发写同一文件时别急着统计。 后台任务还在追加内容,此时跑 wc -l、uniq -c 得到的是快照,前后两次结果会互相矛盾。等 wait 真正返回后再汇总。
时间戳都是 Unix 秒。 macOS 用 date -r <秒> +"%m-%d %H:%M",Linux 用 date -d @<秒>。
统计中文姓名前先设 LC_ALL=C。 否则 sort、uniq 在某些 locale 下行为异常。
十三、实用片段
13.1 查今天谁看过我的文档
#!/bin/bashexport LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1CLI=~/.local/bin/lark-cliTODAY=$(date -j -f "%Y-%m-%d %H:%M:%S""$(date +%Y-%m-%d) 00:00:00" +%s)$CLI drive +search --mine --doc-types docx --page-size 20 --as user 2>/dev/null | \ jq -r '.data.results[] | [.result_meta.token, .title_highlighted] | @tsv' | \while IFS=$'\t'read -r token title; do$CLI drive file.view_records list \ --file-token "$token" --file-type docx --page-size 50 --as user 2>/dev/null | \ jq -r --arg n "$title" --arg d "$TODAY" \'.data.items[]? | select((.last_view_time|tonumber) >= ($d|tonumber)) | [$n, .name, .last_view_time] | @tsv'done13.2 读者排行
输入为上面产出的 TSV(第二列是访问者姓名):
export LC_ALL=CME="<你的名字>"awk -F'\t' -v me="$ME"'$2 != me {print $2}' views.tsv | sort | uniq -c | sort -rn | head -1513.3 时间戳转可读时间
awk -F'\t''{cmd="date -r "$3" +\"%m-%d %H:%M\""; cmd|getline t; close(cmd); printf "%s %-9s %s\n", t, $2, $1}' views.tsv13.4 批量导出文档为本地文件
#!/bin/bashexport LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1CLI=~/.local/bin/lark-climkdir -p ./exports && cd ./exports || exit 1 # 路径参数只收相对路径,必须先 cd# 注意 --page-size 上限 20;title_highlighted 含 <h> 标签,用 gsub 剥掉再当文件名$CLI drive +search --mine --doc-types docx --page-size 20 --as user 2>/dev/null | \ jq -r '.data.results[] | [.result_meta.token, (.title_highlighted | gsub("</?hb?>"; ""))] | @tsv' | \while IFS=$'\t'read -r token title; do safe=$(printf'%s'"$title" | tr'/:''__')$CLI drive +export --file-token "$token" --type docx --output "./${safe}.docx" --as user# 若返回 timed_out=true,用 +task_result --scenario export --ticket <ticket> 继续查done导出整个文件夹(含子目录)的完整流程:
drive files list --folder-token <token> --page-size 200 # 对 type=folder 递归下钻 → drive +export --url <每个文档的 url> --file-extension <fmt> → 若 ready=false:drive +task_result --scenario export --ticket <T> --file-token <token> → drive +export-download --file-token <导出后的 token>
夜雨聆风