Mindket
Claude Code 进阶课第 2 节,共 4 节

Claude Code 无头模式:在脚本和管道里运行 claude -p

黑色横幅,标题是“Claude Code 进阶”,下面一行是“设置、无头运行、CI 与 Agent SDK”。

给 Claude Code 传上 -p,它就不进交互会话:接收一条提示词,跑完智能体循环,打印结果,再用一个脚本能判断的状态码退出。--output-format 选择 text、json 或 stream-json,没人能回答权限提示,所以用 --allowedTools 提前放行工具,--bare 则让每台机器上的运行表现一致。

本节内容

  • claude -p "提示词" 打印答案,成功时退出码为 0,失败时非 0,所以 shell 脚本可以据此分支。
  • 脚本里没人能回答权限提示。用 --allowedTools 提前放行工具,或者设定 dontAsk 这样的模式,否则需要批准的调用会被拒绝。
  • --output-format json 把答案放进 result,同时给出 session_id 和 total_cost_usd。再加 --json-schema,校验过的数据就出现在 structured_output 里。
  • --bare 跳过 hooks、插件、MCP 服务器和 CLAUDE.md,让 CI 在哪里都一样。Anthropic 说它将来会成为 -p 的默认行为。
全部课程
本页目录
  1. 开始之前
  2. 跑一条提示词,把数据管道进去,检查退出码
  3. 没人来批准:工具、模式和提示
  4. 输出格式:text、json 和 stream-json
  5. 继续一段对话,不用猜是哪一段
  6. 用 --bare 让 CI 可重复
  7. 写进脚本,并给它设上限
  8. Claude Code 的作者为什么要每个脚本都加 --bare
  9. 常见错误
  10. 动手试试
  11. 下一课:GitHub Actions 里的 Claude Code
  12. 自测一下
  13. 常见问题
  14. 资料来源

开始之前

你需要 Claude Code、一个终端,以及运行 JSON 示例用的 jq。如果对权限规则还不熟,先读设置那一课,因为无头运行里没人回答提示,全靠规则来决定。

Anthropic 为此写的页面叫 Run Claude Code programmatically。页面把 claude -p 称作 Agent SDK 的命令行形式,第 4 课就建立在 Agent SDK 之上。下面的参数来自这个页面和命令行参考,读于 2026 年 11 月 4 日,无头运行页面在本课更新时又核对过一遍。

跑一条提示词,把数据管道进去,检查退出码

非交互模式会读取 stdin,所以 Claude Code 用起来和任何命令行过滤器一样。下面是 Anthropic 的示例:构建日志送进去,解释以文件的形式出来。

cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
  1. 运行 claude -p "What does the auth module do?"。Claude Code 打印回答然后退出。-p 是 --print 的缩写。
  2. 用上面那条命令把构建日志管道进去。管道输入 stdin 的上限是 10MB,更大的内容请写进文件,并在提示词里写明路径。
  3. 用 echo $? 读退出码。成功是 0,运行失败是非 0。无效的参数会在运行开始之前报到 stderr。
  4. 故意让运行在内部失败,比如在没有认证的情况下运行。Claude Code 会把失败信息当作结果打印到 stdout,所以脚本应该先检查退出状态,再相信 stdout。
  5. 点名工具,让 Claude 修改文件:claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"。

没人来批准:工具、模式和提示

在 Claude Code v2.1.302 或更高版本里,没设权限模式的 -p 运行从 Manual 模式开始。需要批准的调用会被拒绝,除非有 allow 规则、hook、canUseTool 回调或 --permission-prompt-tool 批准它。所以要做的事,是提前说清楚什么可以运行。

在 CI 里,Anthropic 的权限模式页面给了一个配合 dontAsk 的精确白名单。凡是会弹提示的调用都被拒绝,只有列出的那条命令和文件读取能运行。

claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"
在 -p 运行里决定权限的几种办法,来自 headless 页面和命令行参考
参数效果适合
--allowedTools "Read,Edit"列出的工具不经提示直接运行。它用权限规则语法,所以 Bash(git diff *) 放行任何以 git diff 开头的命令命令都已知的任务
--permission-mode acceptEdits文件写入和 mkdir、touch、mv、cp 这类常见文件系统命令直接运行。其他 shell 命令仍然需要 allow 条目lint 修复和重构
--permission-mode dontAsk凡是会弹提示的调用都被拒绝。工作目录里的读取和已放行的工具仍然运行严格锁定的 CI
--permission-mode auto由分类器代替你审查大多数操作没人盯着的宽泛任务
--permission-prompts none凡是会弹提示的调用都被拒绝,并告诉 Claude 不要重试。需要 v2.1.259 或更高版本带权限宿主的定时任务

输出格式:text、json 和 stream-json

默认的 text 输出适合人在终端里看。脚本要的是 json:它返回一个对象,答案在 result 里,另有 session_id、用量元数据、total_cost_usd 和按模型拆分的费用。Anthropic 把这个费用称为客户端估算,可能和账单不一致。

想让数据按你定的形状出来,就加上 --json-schema 并给一个 JSON Schema。校验过的数据出现在 structured_output 字段里。schema 无效时运行会停下,并报 Error: --json-schema is not a valid JSON Schema,而 format 关键字只当注释读,从不强制执行。

claude -p "Extract the main function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
  | jq '.structured_output'
三种输出格式,来自 headless 页面
格式你得到什么怎么读
text纯文本,默认值重定向到文件
json一个对象,含 result、session_id、用量和费用。配合 --json-schema 还多一个 structured_output 字段jq -r '.result'
stream-json按行分隔的 JSON 事件,最后是一条 result 消息。加 --verbose 和 --include-partial-messages 才有逐词的增量用 jq 逐行处理

继续一段对话,不用猜是哪一段

--continue 加载当前目录里最近的一段对话。单独的 --continue 会跳过 claude -p 产生的会话,而 claude -p --continue 把它们算进来。凡是会运行不止一次的任务,都应该从 JSON 输出里取出 ID,恢复那一个确切的会话。

--resume 还接受会话名,或者会话 .jsonl 记录文件的绝对路径。Claude Code 能在这台机器的任何项目里找到某个 ID,所以两条命令可以在不同目录里运行。继续对话时,total_cost_usd 报告的是整段对话的花费,前面几次运行也算在内。--fork-session 从旧对话派生出一个新 ID,--no-session-persistence 则什么都不保存。

session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$session_id"

用 --bare 让 CI 可重复

普通的 claude -p 会加载交互会话加载的东西:hooks、技能、插件、MCP 服务器、自动记忆和 CLAUDE.md,来自工作目录,也来自 ~/.claude。队友的某个 hook 或者一个游离的 .mcp.json 就是这样跑进你的 CI 任务里的。加上 --bare,Claude Code 把这些全部跳过,Claude 保留 Bash、文件读取和文件编辑工具。

bare 模式从不读取 OAuth 凭据或系统钥匙串。请设置 ANTHROPIC_API_KEY,或者在 --settings 的 JSON 里提供 apiKeyHelper。Amazon Bedrock、Google Cloud 的 Agent Platform 和 Microsoft Foundry 仍然读取各自的凭据。v2.1.286 之前这些限制只部分成立,所以请核对你的版本。

没有 --bare 时,-p 会话会运行项目 .claude/settings.json 里的 hooks,并连接其 .mcp.json 里的服务器,即使这个文件夹你从未信任过。

Anthropic, Claude Code 官方文档(此处为中文译文) · Claude Code 文档:Run Claude Code programmatically (2026年11月4日 查阅)
claude --bare -p "Summarize README.md" --allowedTools "Read"
bare 模式下用参数传入的上下文,来自 headless 页面
要加载用
系统提示词的追加内容--append-system-prompt、--append-system-prompt-file
设置--settings,后面跟文件路径或内联 JSON
MCP 服务器--mcp-config
自定义智能体--agents
插件--plugin-dir、--plugin-url

写进脚本,并给它设上限

页面里的这个脚本把一个拉取请求的 diff 管道进 Claude,请它做安全审查。把它存成 review.sh,运行 bash review.sh 123。shell 会把 "$1" 换成 123,审查结果以 JSON 返回,文字在 result 里。

脚本还需要几道上限。文档里写到的有这些。

脚本也离不开它运行时的版本。九月以来有四个版本改了 -p 运行的行为,所以在相信一段旧脚本之前,先在运行器上跑 claude --version,和这些版本对一对。

gh pr diff "$1" | claude -p \
  --append-system-prompt "You are a security engineer. Review for vulnerabilities." \
  --output-format json
  • --max-turns 限制打印模式下的智能体回合数,默认没有上限。
  • --max-budget-usd 在估算花费达到这个金额时停止运行。子智能体的花费也计入,而且花费可能超过上限,所以要留出余量。
  • SIGTERM 会让运行以退出码 143 结束,并且不为正在进行的回合记录结果。想干净地结束回合,先发 SIGINT,或者调用 SDK 的 interrupt()。
  • 给出答案之后,运行还会为它启动的后台工作保持打开。空闲等待默认 10 分钟后停止,CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS 可以改这个上限。
  • stream-json 输出里的 system/init 事件列出已加载的插件和 MCP 服务器。mcp_server_errors 数组非空,说明有服务器根本没加载,所以 CI 关卡可以据此判失败。
近期 Claude Code 版本里和无头运行有关的改动
  1. v2.1.259 新增 --permission-prompts none

    凡是会弹出提示的操作都直接拒绝,其余的仍由你设定的权限模式决定。

    来源:Claude Code release v2.1.259 on GitHub (2026年11月5日 查阅)

  2. v2.1.286 让 --bare 完全生效

    bare 运行只连接命令行上点名的 MCP 服务器,不发系统提醒,也不启动后台任务。

    来源:Claude Code release v2.1.286 on GitHub (2026年11月5日 查阅)

  3. v2.1.314 修好一个无声的启动失败

    启动就失败的 claude -p 运行,以前可能以 0 退出且没有任何输出,现在会打印错误并以 1 退出。

    来源:Claude Code changelog (2026年11月5日 查阅)

  4. v2.1.318 不再乱改 ~/.claude.json

    无人值守的运行,也就是 claude -p 和 SDK 应用,在文件内容没变时不再重写它。

    来源:Claude Code changelog (2026年11月5日 查阅)

Claude Code 的作者为什么要每个脚本都加 --bare

Boris Cherny 是 Claude Code 的作者,他在 X 上的一串技巧里,把 --bare 列为第 12 条。

默认情况下,claude -p 以及 TypeScript 和 Python SDK 会去找本地的 CLAUDE.md、设置和 MCP 服务器。脚本通常更想自己点名要加载什么。

他说这样启动最多能快十倍,还把旧的默认行为称作 SDK 早期的一个设计疏忽。

他在帖子里说,将来的版本会把默认改成 --bare。这一步至今还没到来。无头运行页面仍把它称为脚本的推荐模式和将来的默认值,所以这个参数还得你自己加。

现在就把它加进每一条 CI 调用。默认值改掉的那天,你的任务行为不变。在那之前,队友的 hook 或者随手留下的 .mcp.json 也混不进运行里。

不过在非交互使用时,大多数情况下你会想通过 --system-prompt、--mcp-config、--settings 等参数明确指定要加载什么。

Boris Cherny, Claude Code 的作者,就职于 Anthropic,引文为译文 · X (2026年11月5日 查阅)
Boris Cherny(@bcherny)2026 年 3 月 30 日在 X 上发的第 12 条技巧:用 --bare 让 SDK 启动最多快 10 倍,因为默认情况下 claude -p 和 SDK 会搜索本地的 CLAUDE.md、设置和 MCP,而非交互运行通常想自己指定要加载什么。
Boris Cherny 在 X 上,2026 年 3 月 30 日

X 官方嵌入卡片的截图,拍摄于 2026 年 11 月 5 日,时间为 UTC。X 用“Show more”截断了这条帖子。点击截图可到 X 上打开原帖。

常见错误

  • 用了 --bare 却没有 API 密钥。bare 模式跳过订阅登录,所以在设置 ANTHROPIC_API_KEY 之前运行会认证失败。
  • 没检查 structured_output 是否存在就去读它。运行结束时没有有效的结构化输出,比如 Claude 拒绝了请求,就没有这个字段。
  • 在不是你写的仓库里运行 claude -p。没有 --bare 时,它会运行那个项目的 hooks,并在没有信任对话框的情况下连接它 .mcp.json 里的服务器。请用 --bare 启动,或者传 --setting-sources user。
  • 在共用的目录里依赖 --continue。它选的是那里最近的一段对话,第二个任务一来就变了。请记下会话 ID。
  • 用 SIGTERM 停掉任务,却指望拿到结果。正在进行的回合会被留在未完成的状态。

动手试试

  • 把 git diff main 管道进 claude -p,用 --append-system-prompt 设一份审查员的说明。分别用 text 和 json 各跑一次,比较脚本能拿它们做什么。
  • 写一段 shell,用 --output-format json 运行一条提示词,退出码不是 0 就停下,并用 jq 打印 .result 和 .total_cost_usd。
  • 用 --output-format stream-json --verbose 运行一条提示词,找到 system/init 事件。列出它的插件和 MCP 服务器,再加上 --bare 重复一遍并比较。
  • 在需要更多回合的任务上设 --max-turns 3,记下你的脚本收到了什么。

下一课:GitHub Actions 里的 Claude Code

在 CI 的 runner 上,没人盯着运行,密钥还放在它的环境里,上面的一切都会更难。下一课 GitHub Actions 里的 Claude Code 会搭好官方 action,以及围绕它的密钥、权限和费用限制。

hooks 会在会话的固定节点运行你自己的命令,而 --bare 会跳过它们。hooks 一课列出了 hook 能响应的事件。

自测一下

  1. 一个 CI 任务在 v2.1.302 或更高版本的 Claude Code 里运行 claude -p,没设权限模式,也没放行 Bash。Claude 想运行的 shell 命令会怎样?

    查看答案

    B. 被拒绝,因为运行从 Manual 模式开始,没人能批准 这个版本里没设模式的 -p 运行从 Manual 模式开始,需要批准的调用会被拒绝,除非规则、hook 或回调批准它。

  2. 哪种输出格式返回一个对象,答案在 result 里,并带有 session_id?

    查看答案

    C. json json 格式返回带有结果、会话 ID 和元数据的结构化 JSON,而 stream-json 发送的是按行分隔的事件。

  3. 一个任务运行 claude --bare -p "Summarize README.md",认证失败了。最可能的原因是什么?

    查看答案

    A. bare 模式从不读取 OAuth 凭据或钥匙串,而且没设置 ANTHROPIC_API_KEY bare 模式跳过订阅登录,所以需要环境里的 API 密钥,或者设置 JSON 里的 apiKeyHelper。

  4. 两个任务在同一个目录里运行,第二个必须接着第一个的对话继续。最稳妥的办法是什么?

    查看答案

    D. 从 JSON 输出里读出 session_id,传给 --resume --continue 选的是目录里最近的对话,而带着记下的会话 ID 的 --resume 指明了确切的那一个。

常见问题

什么时候该用 Python 或 TypeScript 包,而不是 claude -p?

当脚本需要的东西,是 shell 给不了的。Anthropic 的页面把工具批准回调和原生消息对象,连同结构化输出,列为这些包额外提供的能力。这门课的最后一节就建立在它们之上。

怎样为某一次运行更换模型或系统提示词?

用 --model 传入 sonnet、opus 或 haiku 这样的别名,或者完整的模型名。--append-system-prompt 把文字追加到默认提示词之后,多数脚本用它就够了,而 --system-prompt 会替换整个默认提示词。

怎样让 claude -p 输出 JSON?

加上 --output-format json。答案在 result 字段里,旁边是 session_id 和费用数据。想要固定的结构,就加 --json-schema 再读 structured_output,运行结束时没有有效的结构化输出,这个字段就不存在。

在 -p 运行里能用技能和斜杠命令吗?

用户调用的技能和自定义命令可以用:在提示词里写 /skill-name,Claude Code 会展开它。只能在终端界面里运行的内置命令,比如 /login,则不可用。/model 等少数几个命令把值当作参数。

怎样追踪一次无头运行花了多少钱?

读 JSON 输出里的 total_cost_usd,里面还有按模型拆分的明细。Anthropic 把这两个数字都称为客户端估算,可能和账单不同。加上 --max-budget-usd,可以在达到上限时停止运行。

能在另一个目录里恢复无头会话吗?

能。--resume 带上会话 ID,会先搜当前项目和它的 git 工作树,再搜这台机器上的所有其他项目。你也可以传入会话 .jsonl 记录文件的绝对路径。

资料来源

  1. Claude Code docs: Run Claude Code programmatically (2026年11月5日 查阅)https://code.claude.com/docs/en/headless
  2. Claude Code docs: CLI reference (2026年11月4日 查阅)https://code.claude.com/docs/en/cli-reference
  3. Claude Code docs: Permission modes (2026年11月4日 查阅)https://code.claude.com/docs/en/permission-modes
  4. Claude Code release v2.1.259 on GitHub (2026年11月5日 查阅)https://github.com/anthropics/claude-code/releases/tag/v2.1.259
  5. Claude Code release v2.1.286 on GitHub (2026年11月5日 查阅)https://github.com/anthropics/claude-code/releases/tag/v2.1.286
  6. Claude Code changelog (2026年11月5日 查阅)https://code.claude.com/docs/en/changelog
  7. Boris Cherny on X: use --bare to speed up SDK startup (2026年11月5日 查阅)https://x.com/bcherny/status/2038454357088457168

分享本节课

适合脚本化运行的技能和工具包

在 -p 运行里把技能名写进提示词,技能就会加载。来看看 Mindket 上的技能。

浏览技能

本节课的 Markdown 版本 · Claude Code 进阶课 · 全部课程