把 Claude Code 放进脚本和 CI:-p 模式的输出格式、预算上限与认证方式
claude -p "query" 跑完就退出,不进交互界面。这是把 Claude Code 放进脚本、CI、git hook 的基础。
但从「能跑」到「能放心让它无人值守跑」,中间有三件事必须做对:
- 成本双保险:
--max-turns拦轮数,--max-budget-usd拦花钱。两个都要设,因为它们拦的不是同一件事。 - 认证方式:CI 里用
claude setup-token生成的长期 token 或 API key,不能依赖交互式登录。 - 权限策略:无人值守时没人能回答权限提示,要么用
--allowedTools白名单,要么用--permission-prompt-tool让一个 MCP 工具代答。
基本调用形态
Section titled “基本调用形态”# 一次性查询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 或名字恢复。
三种输出格式
Section titled “三种输出格式”--output-format 三个取值,用途完全不同。
text(默认):只打印回复文本。适合人看,或者你只需要最终答案。
json:结构化输出,一次性返回。适合脚本解析。
claude -p "list the exported functions in src/api.ts" --output-format jsonstream-json:实时事件流。适合要显示进度、或者要在中途做处理的场景。
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_use 和 tool_result 块,看不到它在想什么。调试「subagent 为什么给了这个结论」时需要它。需要 v2.1.211 或更高版本。
SessionStart 和 Setup 的 hook 事件总是包含在流里,不需要 --include-hook-events。
要固定输出结构,用 —json-schema
Section titled “要固定输出结构,用 —json-schema”--output-format json 给的是 Claude Code 的响应包装结构,不保证回复内容本身的形状。要让模型的输出符合一个 schema:
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"不会真的验证邮箱格式。
两个上限,拦的是两件事
Section titled “两个上限,拦的是两件事”这是无人值守场景最重要的一节。
--max-turns 限制 agentic 轮数,达到上限报错退出:
claude -p --max-turns 3 "fix the failing test"默认无限制。
用 --input-format stream-json 时有一个行为要知道:Claude 工作期间发来的消息会排队,当前轮次因为上限结束时,那条消息作为自己的一轮开始运行,有自己独立的上限。v2.1.205 之前那条消息会被丢弃。
--max-budget-usd 限制 API 花费:
claude -p --max-budget-usd 5.00 "refactor the auth module"subagent 的花费计入这个上限。达到上限后:
- 再起 subagent 会失败,报
Budget limit reached - 仍在运行的后台 subagent 被停止
上限强制行为需要 v2.1.217 或更高版本。
CI 里的认证
Section titled “CI 里的认证”CI 环境不能走交互式登录。两种方式:
长期 OAuth token(需要 Claude 订阅):
claude setup-token这条命令把 token 打印到终端,不保存。把它放进 CI 的 secret 存储。
API key:直接用 Anthropic API key 计费。
官方明确说明:第三方产品(包括基于 Agent SDK 构建的 agent)除事先获批准外,不允许提供 claude.ai 登录或使用其速率额度。所以如果你在构建给别人用的东西,用 API key。
检查认证状态:
claude auth status它输出 JSON,登录时退出码 0,未登录 1。加 --text 得到人类可读的输出。这个退出码语义让它可以直接用在 CI 的前置检查里。
让脚本启动更快
Section titled “让脚本启动更快”两个标志都是「关掉自动发现」,但关的范围不同,用途也不同。
--bare:跳过 hooks、skills、plugins、MCP 服务器、自动记忆、CLAUDE.md 的自动发现。Claude 保留 Bash、文件读、文件编辑工具。
claude --bare -p "reformat this JSON"用途是让脚本调用启动更快。一个只需要处理文本的一次性调用,不需要加载整个项目的配置。
--safe-mode:关掉所有自定义来排查配置问题。
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 或更高版本。
多用户场景的缓存优化
Section titled “多用户场景的缓存优化”--exclude-dynamic-system-prompt-sections 把每机器相关的部分(工作目录、环境信息、记忆路径、git 仓库标记)从系统提示移到第一条用户消息里。
作用是改善 prompt 缓存复用:不同用户、不同机器跑同一个任务时,系统提示变成完全一致的,缓存能命中。
只对默认系统提示生效,设了 --system-prompt 或 --system-prompt-file 时被忽略。适合脚本化的多用户工作负载。
权限:无人值守时怎么办
Section titled “权限:无人值守时怎么办”三种方案,安全性递减:
白名单:明确列出不需要确认的工具。
claude -p --allowedTools "Bash(git log *)" "Bash(git diff *)" "Read" "analyze recent commits"--allowedTools 是「不询问就执行」,--tools 是「限制哪些工具可用」。两个不同的语义,容易混。
要限制可用工具用 --tools:
claude --tools "Bash,Edit,Read"--tools "" 禁用全部内置工具,--tools "default" 是全部。这个标志不影响 MCP 工具——要连 MCP 一起禁,用 --disallowedTools "mcp__*",或者传 --strict-mcp-config 但不给 --mcp-config,这样没有 MCP 服务器加载。
MCP 代答:让一个 MCP 工具处理权限提示。
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。只在一次性容器里用,理由见权限模式。
在 CI 里跑 setup hook
Section titled “在 CI 里跑 setup hook”三个和 hook 相关的 print 模式标志:
| 标志 | 作用 |
|---|---|
--init | 会话前跑带 init matcher 的 Setup hook |
--maintenance | 会话前跑带 maintenance matcher 的 Setup hook |
--init-only | 跑 Setup 和 SessionStart hook 后退出,不开始对话 |
--init-only 的用途是「预热」:在 CI 的一个单独步骤里跑完初始化,后续步骤直接开始工作。
默认情况下会话保存到磁盘可以恢复。CI 里通常不需要:
claude -p --no-session-persistence "query"这个标志只在 print 模式可用。环境变量 CLAUDE_CODE_SKIP_PROMPT_HISTORY 在任何模式下都有同样效果。
需要固定会话 ID 时(比如要在后续步骤恢复):
claude --session-id "550e8400-e29b-41d4-a716-446655440000"必须是合法 UUID。
--bg 把会话作为后台 agent 启动并立即返回,打印会话 ID 和管理命令:
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 会话。
claude --bg --exec 'pytest -x'一个容易踩的命令解析问题
Section titled “一个容易踩的命令解析问题”如果你把 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和不加对比启动耗时。如果差距明显,说明你的项目配置加载成本值得关注。
还没确认的点
Section titled “还没确认的点”--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一样按标准列表价本地计算,文档未见说明。