首页 › tutorials › p模式结构化输出跑批量审查

概述

本教程聚焦 Claude Code 的无头自动化能力:用 -p(print 模式)把 Claude 变成可被脚本调用的"无界面引擎",用 --output-format json --json-schema 让它吐出机器可读的结构化结果,再配一个调度脚本实现跨多文件的批量代码审查,最后用 hooks 给流水线加上确定性的质量门禁与成本核算。学完你能搭出一条"喂一批文件 → 并行审查 → 结构化聚合 → 一份报告"的命令行流水线。适合已经能用交互式 Claude Code、想把重复任务自动化的开发者与工程效能同学。

前置条件

第一步:理解 -p 无头模式的三种输出

交互式 Claude Code 是给人看的;脚本化场景要的是"给一句 prompt、拿一个可解析的结果"。-p(或 --print)就是这把钥匙。它的三种 --output-format 决定了你怎么消费结果:

# text:纯文本,最省事,适合直接 > 到文件或拼接
claude -p "用一句话总结 auth.py 的作用" --output-format text

# json:单条 JSON,含 result / session_id / total_cost_usd / usage
claude -p "总结这个项目" --output-format json | jq -r '.result'

# stream-json:逐行 JSON 事件流,适合实时进度展示
claude -p "解释递归" --output-format stream-json --verbose

批量任务的核心是 json 格式:因为它把"答案"和"元数据"(session id、token 用量、total_cost_usd 花费)打包在一起,你既能取结果,又能事后核算成本,而不必去后台 Dashboard 查。

预期结果:运行 --output-format json 会返回一段形如 {"type":"result","result":"...","session_id":"...","total_cost_usd":0.0123,"usage":{...}} 的 JSON。

常见问题: - 提示要权限确认,脚本卡住:加 --allowedTools "Read" 等把要用的工具预批准掉,批量任务才不会中途停。 - 管道喂的内容太大被拒:自 v2.1.128 起 stdin 上限 10MB,超过会非零退出。解法是把大文件写到磁盘,在 prompt 里引用路径而不是用管道。

第二步:用 --json-schema 锁定结构化输出

批量审查最怕"每次返回格式不一样"。--json-schema 让你给 Claude 一份 JSON Schema,它会强制把答案塞进这个结构,结果出现在 structured_output 字段里。这一步是把"会读代码的助手"变成"会填表的工人"的关键。

claude --bare -p "审查 src/auth.py 的安全问题" \
  --allowedTools "Read" \
  --output-format json \
  --json-schema '{
    "type":"object",
    "properties":{
      "severity":{"type":"string","enum":["high","medium","low"]},
      "findings":{
        "type":"array",
        "items":{
          "type":"object",
          "properties":{
            "file":{"type":"string"},
            "line":{"type":"integer"},
            "issue":{"type":"string"},
            "suggestion":{"type":"string"}
          },
          "required":["file","line","issue","suggestion"]
        }
      },
      "summary":{"type":"string"}
    },
    "required":["severity","findings","summary"]
  }' | jq '.structured_output'

注意开头的 --bare:它会跳过对 hooks、skills、plugins、MCP、CLAUDE.md 的自动发现,启动更快、行为更确定——同一台机器和不同机器跑出来的结果一致,这正是 CI 与脚本最想要的特性。--bare 也被官方定为未来 -p 的默认模式。

预期结果:jq '.structured_output' 输出符合 schema 的对象,severity 是枚举之一,findings 是数组,每条都有 file/line/issue/suggestion。

常见问题: - 偶尔字段缺失:Schema 里放进 required 的字段偶尔会被模型省略,可在解析脚本里对缺失字段填默认值(如 "line":0),别让 jq 报错中断整批。 - 想读多个文件:--allowedTools "Read" 加上 prompt 里列出路径即可;不要把整个仓库 pipe 进来。

第三步:写批处理调度脚本(并行 + 聚合)

有了"单文件结构化审查"的能力,批量化就是把"文件列表"喂给"调度器"。下面这个脚本用 xargs -P 做并发,每个文件一个 -p 调用,结果用 jq 抽取后追加进一份汇总 JSONL,最后渲染成 Markdown 报告。

#!/usr/bin/env bash
# batch-review.sh —— 批量结构化代码审查
set -euo pipefail

GLOB="${1:-src/**/*.py}"          # 第一个参数:文件通配
CONCURRENCY="${2:-4}"             # 第二个参数:并发度
OUT="review-$(date +%Y%m%d-%H%M%S).jsonl"
REPORT="report.md"

SCHEMA='{"type":"object","properties":{"severity":{"type":"string","enum":["high","medium","low"]},"findings":{"type":"array","items":{"type":"object","properties":{"file":{"type":"string"},"line":{"type":"integer"},"issue":{"type":"string"},"suggestion":{"type":"string"}},"required":["file","line","issue","suggestion"]}},"summary":{"type":"string"}},"required":["severity","findings","summary"]}'

review_one() {
  local f="$1"
  # 每个 -p 调用都吐出完整 json:我们只保留 structured_output + 成本
  claude --bare -p "审查文件 $f 的安全与质量问题,只针对该文件。" \
    --allowedTools "Read" \
    --output-format json \
    --json-schema "$SCHEMA" \
    | jq -c --arg f "$f" '{
        file:$f,
        severity:.structured_output.severity,
        findings:.structured_output.findings,
        summary:.structured_output.summary,
        cost:.total_cost_usd
      }'
}
export -f review_one
export SCHEMA

# 1) 发现文件 + 并行审查 → 每行一条 JSON 写入 OUT
# shellcheck disable=SC2086
printf '%s\n' $GLOB | xargs -I{} -P"$CONCURRENCY" bash -c 'review_one "$@"' _ {} >> "$OUT"

# 2) 渲染报告:按 severity 排序、列出 findings、核算总成本
{
  echo "# 批量代码审查报告"
  echo
  echo "审查文件数:$(jq -s 'length' "$OUT")"
  echo "高危项数:$(jq -sr 'map(select(.severity=="high"))|length' "$OUT")"
  echo "总成本:\$$(jq -sr '[.[].cost]|add // 0' "$OUT")"
  echo
  echo "## 高危问题"
  jq -sr 'map(select(.severity=="high").findings[])|
          map("| \(.file):\(.line) | \(.issue) | \(.suggestion) |")|
          "---\n| 文件:行 | 问题 | 建议 |\n|---|---|---|\n" + (join("\n"))' "$OUT"
} > "$REPORT"

echo "完成:详见 $REPORT"

预期结果:跑完得到 review-*.jsonl(每行一个文件的结构化结果)和 report.md(含统计、成本、高危表格)。把并发度 4 调大可缩短墙钟时间,但要注意 API 速率限制。

常见问题: - --bare 下背景任务没跑完就被杀:自 v2.1.182 起,-p 对后台子代理的等待上限默认 10 分钟(环境变量 CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS 控制,设 0 表示不限)。如果你的审查里 Claude 自己起了后台子代理,注意这个上限。 - 某个文件审查失败导致 set -e 全盘退出:在 review_one 里把 claude ... 的退出码吞掉(如 ... || echo '{}'),别让单点失败拖垮整批。

第四步:给流水线加 hooks(关键陷阱:bare 模式会跳过 hooks)

批量审查是只读的,用 --bare 没问题。但如果下一步你想让 Claude 批量自动修复问题并落盘,就需要 Edit/Write 工具,这时候 hooks(如编辑后自动格式化)才派得上用场。这里有一个最容易踩的坑:

--bare 会跳过所有 hooks,因为它根本不读配置。要让 hook 生效,要么不用 --bare,要么用 --settings 显式注入。

下面给自动修复场景配一个 PostToolUse 钩子:每次 Edit/Write 之后自动跑格式化。把它写进项目的 .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs -r npx prettier --write"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": "echo \"[audit] $(date) 修复任务结束\" >> .claude/audit.log" }
        ]
      }
    ]
  }
}

然后批量自动修复时故意不带 --bare,并显式带上工具白名单:

claude -p "审查并直接修复 src/auth.py 里的高危安全问题,保存修改" \
  --allowedTools "Read,Edit,Bash" \
  --output-format json > fix-auth.json

这样每次 Edit 之后,PostToolUse 钩子都会确定性地跑一遍格式化——不依赖模型"记得"去格式化,而是强制保证。Stop 钩子则在每轮结束时写审计日志,方便你事后核对哪个文件被改过。

预期结果:文件被修改后会自动经过 prettier;.claude/audit.log 里按时间记录每次修复任务的结束。

常见问题: - 配了 hook 却不触发:多半是你同时用了 --bare。要么去掉 --bare,要么改用 claude -p ... --settings ./.claude/settings.json 把配置显式喂进去。 - /hooks 里看不到:交互式 claude 下输入 /hooks 是只读浏览器,能看到事件计数;若显示无配置,说明路径或 JSON 写错了,先在交互模式里验证再搬到脚本。

第五步:用 stream-json 做实时进度,用成本字段做核算

批量大时,干等到脚本结束才看到结果很焦虑。stream-json 能逐行吐事件,让你边跑边看进度;而每个 json 输出里的 total_cost_usd 让你能在报告里直接核算总花费。把审查脚本改造一下,加一个进度探针:

# 实时流式 + 只取最终 result 事件,同时累计成本
claude --bare -p "审查 src/payments.py" \
  --allowedTools "Read" \
  --output-format stream-json --verbose \
  | jq -rc --unbuffered 'select(.type=="assistant")'

整批跑完后,前面脚本里已经用 jq -sr '[.[].cost]|add' 算出了总成本。这就形成闭环:每条结果带成本 → 聚合时求和 → 报告里直接显示花了多少钱,无需登录后台。

实战案例:给一个 Python 服务批量体检

假设你有一个微服务仓库,src/ 下有十几个模块,安全同学让你一次性把"硬编码密钥、SQL 拼接、未校验输入"扫一遍并出报告。

  1. 在仓库根目录建好上面的 batch-review.sh。
  2. 执行 ./batch-review.sh 'src/**/*.py' 6,并发 6 跑起来。
  3. 期间另开一个终端 tail -f .claude/audit.log(如果你启用了 Stop 钩子)或观察脚本滚动输出。
  4. 跑完打开 report.md:顶部是统计(文件数、高危数、总成本),下面是按高危优先排序的问题表格,每行带 文件:行 可直接跳转。
  5. 对高危项,挑出来跑第四步的自动修复流程(不带 --bare 以触发格式化钩子),用 --resume 或 --continue 在同一 session 里继续追问修复是否彻底。

这条流水线的妙处在于:审查(只读、bare、快且确定) 与 修复(可写、带 hooks、强制质量门禁) 用两种配置分开处理,既快又稳。

工作流程图

flowchart TD
    A["文件列表<br/>src/**/*.py"] --> B["batch-review.sh<br/>xargs -P 并发"]
    B --> C["每个文件一个<br/>claude --bare -p"]
    C -->|"--output-format json"| D["结构化输出<br/>structured_output"]
    C -->|"total_cost_usd"| E["成本字段"]
    D --> F["jq 聚合<br/>→ review.jsonl"]
    E --> F
    F --> G["渲染 report.md<br/>统计+高危表+总成本"]
    G --> H{需要自动修复?}
    H -->|是| I["claude -p 修复<br/>不带 --bare"]
    I --> J["PostToolUse 钩子<br/>自动格式化"]
    J --> K["Stop 钩子<br/>写审计日志"]
    H -->|否| L["交付报告"]
    K --> L

延伸阅读

训练营适用性评估

参考来源

  1. Run Claude Code programmatically — Claude Code Docs — 官方 -p / --bare / --output-format / --json-schema 与成本字段、stdin 上限、后台任务等待上限的权威说明。
  2. Hooks reference — Claude Code Docs — Hook 事件全集、settings.json 配置 schema、matcher/if 过滤与退出码、PostToolBatch 等事件定义。
  3. Automate actions with hooks — Claude Code Docs — hooks 实战入门,含 PostToolUse 自动格式化、Stop 通知、matcher 与 if 字段的可用示例。
  4. Claude Code Headless Mode: Non-Interactive & CI Usage (2026) — 第三方对 -p 在 CI/CD、脚本编排与管道集成场景的补充实践。
  5. Using Claude Code Programmatically — Gist — --print --output-format 在真实脚本中绕过 TUI、做结构化消费的工程经验。

汇总:高风险与 critical 项

jq -s ' [.[] | .structured_output // empty] | map(select(.risk_level=="high" or .risk_level=="critical")) | {critical_repos: [.[] | .repo], total_findings: ([.[].findings[]] | length), cost_usd: ([$$COST_PLACEHOLDER] | .[0])} ' reports/*.json > summary.json echo "完成。高风险仓库见 summary.json"

**3) 预期结果**

- `reports/<repo>.json`:每个仓库一份结构化报告,含 `risk_level`、`findings[]`。
- `summary.json`:高风险/critical 仓库清单、发现总数。
- 终端逐行打印 `[OK]/[FAIL]`,失败有独立 `.err` 可查。

**4) 常见问题与解决**

| 现象 | 原因 | 解决 |
| --- | --- | --- |
| 大量 `[FAIL]` + err 里 `rate_limit` | 并发过高触发限流 | 降 `-P` 到 2~3,或加退避 |
| `structured_output` 为空 | schema 太宽或仓库过大超 `--max-turns` | 调大 turns、收紧 schema、拆分仓库 |
| 账单高于预期 | 没设 `--max-budget-usd` | 每任务加预算上限 |
| 结果不稳定 | 未用 `--bare` | 全程 `--bare`,CI 可复现 |

---

## 整体工作流程

```mermaid
flowchart TD
    A["repos/ 多仓库目录"] --> B["find 遍历目录列表"]
    B --> C["xargs -P 4 并发扇出"]
    C --> D1["claude --bare -p\n+ allowedTools Read\n+ json-schema\n+ max-budget/turns"]
    C --> D2["claude --bare -p ..."]
    C --> D3["claude --bare -p ..."]
    D1 --> E1["reports/repo1.json"]
    D2 --> E2["reports/repo2.json"]
    D3 --> E3["reports/repo3.json"]
    E1 & E2 & E3 --> F["jq -s 汇总"]
    F --> G["summary.json\n高风险仓库 / 发现总数 / 成本"]
    G --> H["阈值告警 / 报表 / 数据库"]

    subgraph 单次调用生命周期
      I["system/init\n(model/tools/plugins)"] --> J["assistant 文本+工具循环"]
      J --> K{"可重试错误?"}
      K -- 是 --> L["system/api_retry\n(退避后重试)"] --> J
      K -- 否 --> M["result\n(total_cost_usd/duration)"]
    end

延伸阅读

训练营适用性评估

参考来源

  1. Run Claude Code programmatically — 官方 Headless 文档 — -p、--bare、--output-format、--json-schema、total_cost_usd、后台任务宽限等权威说明。
  2. CLI reference — Claude Code Docs — 全部 flag(--session-id/--resume/--continue/--fork-session/--max-turns)的完整参考。
  3. Claude Code Stream-JSON Protocol Reference — claudewerk — 逆向自 CC v2.1.16x 的 stream-json 输入/输出协议、control_request 握手与事件目录。
  4. Claude stream-json event cheatsheet — takopi — system/init、api_retry、result 等顶层事件字段速查。
  5. Claude Code Headless Mode: The Definitive Guide to claude -p — Build This Now — -p 实战模式、jq 解析、stdin 管道与 SDK 迁移路径。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入