跳转到内容

把 Claude Code 放进脚本和 CI:-p 模式的输出格式、预算上限与认证方式

约 22 分钟 难度:硬核 动手章

claude -p "query" 跑完就退出,不进交互界面。这是把 Claude Code 放进脚本、CI、git hook 的基础。

但从「能跑」到「能放心让它无人值守跑」,中间有三件事必须做对:

  1. 成本双保险--max-turns 拦轮数,--max-budget-usd 拦花钱。两个都要设,因为它们拦的不是同一件事。
  2. 认证方式:CI 里用 claude setup-token 生成的长期 token 或 API key,不能依赖交互式登录。
  3. 权限策略:无人值守时没人能回答权限提示,要么用 --allowedTools 白名单,要么用 --permission-prompt-tool 让一个 MCP 工具代答。
Terminal window
# 一次性查询
claude -p "explain this function"
# 管道输入
cat logs.txt | claude -p "find the root cause of the errors"
# 继续最近一次对话
claude -c -p "check for type errors"
# 按 ID 或名字恢复会话
claude -r "auth-refactor" "finish this PR"

-c--continue,加载当前目录最近的对话。-r--resume,按会话 ID 或名字恢复。

--output-format 三个取值,用途完全不同。

text(默认):只打印回复文本。适合人看,或者你只需要最终答案。

json:结构化输出,一次性返回。适合脚本解析。

Terminal window
claude -p "list the exported functions in src/api.ts" --output-format json

stream-json:实时事件流。适合要显示进度、或者要在中途做处理的场景。

Terminal window
claude -p --output-format stream-json --verbose "run the test suite and summarize failures"

stream-json 有一批配套标志,都要求同时带 --verbose

标志作用
--include-partial-messages包含流式的部分消息事件
--include-hook-events包含所有 hook 生命周期事件
--forward-subagent-text输出 subagent 的文本和 thinking 块,可以重建每个 subagent 的 transcript
--replay-user-messages把 stdin 的用户消息回显到 stdout 用于确认
--prompt-suggestions每轮之后输出一条预测的下一句用户输入

--forward-subagent-text 值得留意:不加它,你只能看到 subagent 的 tool_usetool_result 块,看不到它在想什么。调试「subagent 为什么给了这个结论」时需要它。需要 v2.1.211 或更高版本。

SessionStartSetup 的 hook 事件总是包含在流里,不需要 --include-hook-events

要固定输出结构,用 —json-schema

Section titled “要固定输出结构,用 —json-schema”

--output-format json 给的是 Claude Code 的响应包装结构,不保证回复内容本身的形状。要让模型的输出符合一个 schema:

Terminal window
claude -p --json-schema '{"type":"object","properties":{"severity":{"type":"string"},"files":{"type":"array","items":{"type":"string"}}}}' \
"analyze the security issues in this diff"

这个功能只在 print 模式可用。

两个细节:

  • schema 无效时 Claude Code 直接报错退出。v2.1.205 之前它会静默产出非结构化输出,不报错——这是个很难发现的坑。
  • format 关键字被接受,但只作为注解,不做客户端校验。所以 "format": "email" 不会真的验证邮箱格式。

这是无人值守场景最重要的一节。

--max-turns 限制 agentic 轮数,达到上限报错退出:

Terminal window
claude -p --max-turns 3 "fix the failing test"

默认无限制。

--input-format stream-json 时有一个行为要知道:Claude 工作期间发来的消息会排队,当前轮次因为上限结束时,那条消息作为自己的一轮开始运行,有自己独立的上限。v2.1.205 之前那条消息会被丢弃。

--max-budget-usd 限制 API 花费:

Terminal window
claude -p --max-budget-usd 5.00 "refactor the auth module"

subagent 的花费计入这个上限。达到上限后:

  • 再起 subagent 会失败,报 Budget limit reached
  • 仍在运行的后台 subagent 被停止

上限强制行为需要 v2.1.217 或更高版本。

CI 环境不能走交互式登录。两种方式:

长期 OAuth token(需要 Claude 订阅):

Terminal window
claude setup-token

这条命令把 token 打印到终端,不保存。把它放进 CI 的 secret 存储。

API key:直接用 Anthropic API key 计费。

官方明确说明:第三方产品(包括基于 Agent SDK 构建的 agent)除事先获批准外,不允许提供 claude.ai 登录或使用其速率额度。所以如果你在构建给别人用的东西,用 API key。

检查认证状态:

Terminal window
claude auth status

它输出 JSON,登录时退出码 0,未登录 1。加 --text 得到人类可读的输出。这个退出码语义让它可以直接用在 CI 的前置检查里。

两个标志都是「关掉自动发现」,但关的范围不同,用途也不同。

--bare:跳过 hooks、skills、plugins、MCP 服务器、自动记忆、CLAUDE.md 的自动发现。Claude 保留 Bash、文件读、文件编辑工具。

Terminal window
claude --bare -p "reformat this JSON"

用途是让脚本调用启动更快。一个只需要处理文本的一次性调用,不需要加载整个项目的配置。

--safe-mode:关掉所有自定义来排查配置问题。

Terminal window
claude --safe-mode

它和 --bare 的关键差别:--safe-mode 下认证、模型选择、内置工具、权限正常工作,而且 managed 设置策略仍然生效(含策略配置的 hook、status line)。managed 插件、managed skill、managed CLAUDE.md、策略配置的 MCP 服务器不加载。

所以:脚本用 --bare,排查配置用 --safe-mode

--safe-mode 有一个具体用途值得记住:检查某个自定义配置是不是导致了自动模型回退。

还有一个更细的控制:--setting-sources user,project 明确指定加载哪些设置层。

系统提示:四个标志与一个判断

Section titled “系统提示:四个标志与一个判断”
标志行为
--system-prompt替换整个默认提示
--system-prompt-file用文件内容替换
--append-system-prompt追加到默认提示后
--append-system-prompt-file把文件内容追加到默认提示后

--system-prompt--system-prompt-file 互斥。追加类标志可以和替换类组合。

判断标准很清楚:Claude Code 的默认身份还适合你的任务吗

用追加:Claude 仍然是一个编码助手,只是多遵守你的额外规则。追加保留了默认的工具指导、安全指令和编码约定,你只提供差异部分。

用替换:任务的界面、身份或权限模型与 Claude Code 不同,比如一个流水线里无人监督的非编码 agent。替换会丢掉全部默认提示,包括工具指导和安全指令,你要为任务还需要的一切负责。

subagent 也有对应的标志:--append-subagent-system-prompt 追加文本到每个 subagent 的系统提示末尾,含嵌套的。它只在 -p 非交互模式下生效,需要 v2.1.205 或更高版本。

--exclude-dynamic-system-prompt-sections 把每机器相关的部分(工作目录、环境信息、记忆路径、git 仓库标记)从系统提示移到第一条用户消息里。

作用是改善 prompt 缓存复用:不同用户、不同机器跑同一个任务时,系统提示变成完全一致的,缓存能命中。

只对默认系统提示生效,设了 --system-prompt--system-prompt-file 时被忽略。适合脚本化的多用户工作负载。

三种方案,安全性递减:

白名单:明确列出不需要确认的工具。

Terminal window
claude -p --allowedTools "Bash(git log *)" "Bash(git diff *)" "Read" "analyze recent commits"

--allowedTools 是「不询问就执行」,--tools 是「限制哪些工具可用」。两个不同的语义,容易混。

要限制可用工具用 --tools

Terminal window
claude --tools "Bash,Edit,Read"

--tools "" 禁用全部内置工具,--tools "default" 是全部。这个标志不影响 MCP 工具——要连 MCP 一起禁,用 --disallowedTools "mcp__*",或者传 --strict-mcp-config 但不给 --mcp-config,这样没有 MCP 服务器加载。

MCP 代答:让一个 MCP 工具处理权限提示。

Terminal window
claude -p --permission-prompt-tool mcp_auth_tool "query"

Claude Code 会等那个工具的 MCP 服务器连上才跑第一轮,上限是 MCP_TIMEOUT 的 30 秒启动超时。v2.1.206 之前,一个启动慢的服务器会让运行以「MCP 工具未找到」的错误退出。

一个限制:这个工具不能批准标记为需要用户交互的 MCP 工具——Claude Code 会把对那类工具的 allow 结果转成 deny。需要 v2.1.199 或更高版本。

跳过全部--dangerously-skip-permissions。只在一次性容器里用,理由见权限模式

三个和 hook 相关的 print 模式标志:

标志作用
--init会话前跑带 init matcher 的 Setup hook
--maintenance会话前跑带 maintenance matcher 的 Setup hook
--init-only跑 Setup 和 SessionStart hook 后退出,不开始对话

--init-only 的用途是「预热」:在 CI 的一个单独步骤里跑完初始化,后续步骤直接开始工作。

默认情况下会话保存到磁盘可以恢复。CI 里通常不需要:

Terminal window
claude -p --no-session-persistence "query"

这个标志只在 print 模式可用。环境变量 CLAUDE_CODE_SKIP_PROMPT_HISTORY 在任何模式下都有同样效果。

需要固定会话 ID 时(比如要在后续步骤恢复):

Terminal window
claude --session-id "550e8400-e29b-41d4-a716-446655440000"

必须是合法 UUID。

--bg 把会话作为后台 agent 启动并立即返回,打印会话 ID 和管理命令:

Terminal window
claude --bg "investigate the flaky test"

--bg 不能和 -p 组合。两者的语义冲突:-p 是「跑完就退」,--bg 是「丢到后台继续跑」。

配套的管理命令:

命令作用
claude agents打开 agent view 监控和派发并行后台会话。--json 打印活跃会话的 JSON 数组
claude attach <id>在当前终端接入一个后台会话
claude logs <id>打印后台会话的近期输出
claude stop <id>停止后台会话
claude respawn <id>重启后台会话,对话历史保留
claude rm <id>从列表移除,transcript 仍在本地

claude agents --json 是脚本化的入口。claude respawn --all 重启所有运行中的会话,典型用途是让它们用上更新后的 Claude Code 二进制。

--exec 是另一个方向:把一个 shell 命令作为 PTY 支持的后台作业跑,而不是启动 Claude 会话。

Terminal window
claude --bg --exec 'pytest -x'

如果你把 claude 别名成带 --dangerously-skip-permissions 的形式,claude daemon status 这类子命令在旧版本上不会执行——v2.1.199 之前,daemon <subcommand> 会被当成新交互式会话的提示词。

v2.1.199 起 claude --dangerously-skip-permissions daemon <subcommand> 正确路由到 daemon 子命令。但只有前置的 --dangerously-skip-permissions--allow-dangerously-skip-permissions 会这样路由,其他前置标志仍然启动交互式会话。

另外 claude --help 不列出全部标志。一个标志不在 --help 里,不代表它不可用。这一点在排查「文档提到的标志报错」时很关键——先确认版本,而不是怀疑文档。

  • 写一个 git pre-commit hook 用 claude -p --output-format json 检查暂存的 diff,设好 --max-turns 2--max-budget-usd 0.20。跑几次看上限是不是符合预期。
  • --json-schema 让输出固定成一个你能直接喂给下游脚本的结构,对比不用 schema 时解析的脆弱程度。
  • 同一个任务分别用 --bare 和不加对比启动耗时。如果差距明显,说明你的项目配置加载成本值得关注。
  • --output-format json 返回结构的完整字段。CLI reference 说明了格式选项,字段清单在 Agent SDK 文档里,本文未核实。
  • stream-json 各事件类型的完整定义。同上。
  • print 模式下的退出码语义。只确认了 claude auth status(0 登录 / 1 未登录)和 claude ultrareview(0 成功 / 1 失败)这两个子命令,claude -p 本身在各类失败下的退出码没有查到明确说明。
  • --max-budget-usd 的计价基准。它是否和 /usage 一样按标准列表价本地计算,文档未见说明。
这一节有错或讲不清? 提个 Issue 直接改文档 请作者催更