首页 › tutorials › -p 锁 JSON 造笔记索引管道

概述

本教程教你把 Claude Code 从"交互式聊天框"变成"可组合的命令行数据工具":用 -p 模式程序化调用、用 --json-schema 把输出锁死成结构化数据、用 jq 把 Claude 串进 Unix 管道,最后挂一个成本账本 hook 让批量调用可观测。适合已经会用 Claude Code 交互模式、想把它接进脚本与数据流水线的开发者、运维人和数据工程师。

前置条件

一、为什么要把 Claude Code 当 Unix 工具

Unix 哲学的核心是"每个程序做好一件事,通过文本流串联"。grep、awk、jq 都是这个传统的产物。Claude Code 的 -p(print)模式让它也具备了成为管道一环的能力:读 stdin、写 stdout、退出码规范、输出可结构化。这意味着你不必把 Claude 当成一个需要手动伺候的对话框,而可以让它和 git、jq、xargs、curl 并肩工作。

举个直觉:你想给 100 篇笔记各生成一份"标题 + 摘要 + 标签"的结构化卡片,传统做法是开 100 次对话框复制粘贴。换成管道思维,就是"喂文件 → Claude 提取 → jq 落库 → 聚合成索引",整条链一行 for 循环就能跑完,而且每一步都可单独替换、可重放、可监控。

二、第一步:跑通最简 -p 调用

-p(或 --print)让 Claude Code 走非交互模式:吃一条 prompt,吐一段结果,然后退出。所有交互模式下的 CLI 选项在 -p 里都可用。

# 最简单的调用
claude -p "What does the auth module do?"

# 通过 stdin 喂数据(推荐,避免转义地狱)
cat notes/2026-07-01-plan.md | claude -p "用一句话总结这篇笔记"

预期结果:终端直接打印总结文本,进程退出。-p 模式会读取 stdin,所以 cat 文件 | claude -p 提问 是最稳的喂料姿势。

关于 --bare 的取舍:默认情况下,claude -p 会加载交互式会话的全部上下文——~/.claude 里的 hooks、skills、plugins、MCP 服务器、CLAUDE.md。这在脚本里反而是隐患:同事机器上配的某个 hook 可能悄悄改变结果。加 --bare 会跳过所有自动发现,只认你显式传入的 flag:

# 可复现、零惊喜:CI 与脚本的首选
claude --bare -p "Summarize this file" --allowedTools "Read" < notes/x.md

官方明确:--bare 是脚本与 SDK 调用的推荐模式,未来会成为 -p 的默认值。但 --bare 会跳过 hooks——所以本文的"成本账本 hook"那一步,需要你在交互或非 bare 模式下才能生效。这个权衡后面会再提到。

⚠️ 注意:从 v2.1.128 起,-p 的 stdin 输入上限是 10MB。超过会以非零退出码报错。大文件请写盘后在 prompt 里引用路径,而不是 cat 进去。

常见问题:

三、第二步:用 --output-format json 拿结构化结果

默认 -p 吐纯文本,适合人看,不适合程序解析。加 --output-format json 后,你会拿到一个结构化对象:

claude -p "Summarize this project" --output-format json

输出大致长这样:

{
  "type": "result",
  "subtype": "success",
  "result": "这是一个用 FastAPI 写的待办后端……",
  "session_id": "abc123de-4567-…",
  "total_cost_usd": 0.0123,
  "duration_ms": 4380,
  "is_error": false
}

这里有几个关键字段:result 是文本结果,session_id 可用于 --resume 续跑,total_cost_usd 是本次调用的花费,duration_ms 是耗时。total_cost_usd 是批量调用做成本核算的金矿——脚本里不必再去翻用量后台。

配合 jq 提取你想要的字段:

# 只要文本结果
claude -p "Summarize this project" --output-format json | jq -r '.result'

# 只要 session id,留给后续 --resume
sid=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$sid"

预期结果:jq -r '.result' 干净地剥掉外壳,只剩纯文本;$sid 变量里是一个会话 ID,可用于多轮续跑。

常见问题:

四、第三步:用 --json-schema 锁死输出契约(核心)

让大模型自由发挥写 JSON 是灾难:它会多塞字段、漏字段、把数组写成对象、把布尔写成 "true"。--json-schema 直接把输出绑死到一个 JSON Schema 定义上,结果落在响应的 structured_output 字段里。

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"]
  }'

响应里你会看到:

{
  "result": "…",
  "structured_output": { "functions": ["login", "verify_token", "refresh"] },
  "session_id": "…",
  "total_cost_usd": 0.008
}

jq 取结构化部分:

claude -p "Extract function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
  | jq '.structured_output.functions'

预期结果:无论模型多想啰嗦,structured_output 永远严格符合你定义的 schema——数组就是数组,必填字段一定在。这是把"AI 输出"变成"可信数据"的关键一步,也是本文与普通批量调用最大的区别。

💡 把 schema 存成文件更易维护:--json-schema "$(cat schemas/note-card.json)",schema 进版本控制,团队共享同一份契约。

常见问题:

五、第四步:把 Claude 串进 Unix 管道

有了结构化输出,Claude 就能和经典 Unix 工具平等协作。下面这条链:git diff 喂给 Claude 当 typo 检查器,结果再交给 jq 提取——Claude 只是管道中的一节。

# 把当前 diff 丢给 Claude 当拼写检查器
git diff main | claude -p \
  "you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else."

# 接进管道:Claude 出结构化结果 → jq 过滤 → wc 数量
cat notes/plan.md | claude -p "提取这篇笔记涉及的 所有 技术名词,去重" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"terms":{"type":"array","items":{"type":"string"}}},"required":["terms"]}' \
  | jq '.structured_output.terms | length'

预期结果:第一条命令在终端逐条列出 diff 里的疑似拼写错误;第二条命令输出一个整数——笔记里涉及的技术名词数量。

这种"小而专"的调用比"一个大 prompt 干所有事"更稳:每节管道职责单一,坏了容易定位,换模型/换工具只动一节。

常见问题:

六、第五步:批量遍历目录聚合成 index.json

把前几步拼起来:遍历 notes/*.md,每篇调一次 Claude 提取结构化卡片,最后聚合成一个站点索引。先用单篇验证:

process_one() {
  local f="$1"
  cat "$f" | claude -p "你是笔记编目员。读取这篇笔记,提取标题、一句话摘要、3-5个标签、涉及的核心技术。纯文本,不要编造。" \
    --output-format json \
    --bare \
    --allowedTools "Read" \
    --json-schema "$(cat schemas/note-card.json)" \
    | jq -c --arg src "$f" '.structured_output + {source: $src}'
}

对应的 schemas/note-card.json:

{
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "summary": { "type": "string" },
    "tags": { "type": "array", "items": { "type": "string" } },
    "tech": { "type": "array", "items": { "type": "string" } }
  },
  "required": ["title", "summary", "tags", "tech"]
}

jq -c 把每篇压成一行 JSON(JSONL),--arg src 把文件名塞进每条记录。批量遍历并聚合成数组:

# 串行版:稳,慢
for f in notes/*.md; do process_one "$f"; done > cards.jsonl

# 聚合成一个 index.json
jq -s '{generated_at: (now | todate), count: length, notes: .}' cards.jsonl > index.json

jq -s(slurp)把多行 JSONL 收成一个数组,now | todate 给索引盖上时间戳。

并发加速:笔记多了,串行会很慢。用 xargs -P 并发(-P 4 表示同时 4 个):

find notes -name '*.md' -print0 \
  | xargs -0 -n1 -P4 -I{} bash -c 'process_one "$1"' _ {} \
  >> cards.jsonl

预期结果:cards.jsonl 里每行一篇笔记的结构化卡片;index.json 是一个带时间戳和总数的完整索引,可直接喂给静态站点生成器做搜索。

常见问题:

七、第六步:给批量调用挂一个成本账本 hook

批量跑了几百篇笔记,你肯定想知道花了多少钱、哪篇最贵。-p 的 JSON 输出里有 total_cost_usd,但那是给程序读的;如果你想自动、持续地记账,就用 hook。

Claude Code 的 hooks 配在 settings.json 里,按事件分组。我们用 Stop 事件——每次 Claude 停止响应时触发,hook 通过 stdin 收到一个 JSON,里面带 transcript_path 指向本次会话的完整记录(JSONL)。我们从中提取最后一轮的 token 用量,写进账本。

.claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/cost-ledger.sh"
          }
        ]
      }
    ]
  }
}

.claude/cost-ledger.sh:

#!/usr/bin/env bash
# 从 stdin 读 hook input,提取 transcript,算 token 成本,追加到账本
input=$(cat)
transcript=$(printf '%s' "$input" | jq -r '.transcript_path')
session_id=$(printf '%s' "$input" | jq -r '.session_id')

# 从 transcript 取最后一条带 usage 的 assistant 消息
usage=$(jq -s 'map(select(.message.usage // empty)) | last | .message.usage' "$transcript" 2>/dev/null)

[ -z "$usage" ] || [ "$usage" = "null" ] && exit 0

# 简化定价(USD / 1M tokens),按需替换为你当前模型的真实价目
in=$(printf '%s' "$usage"   | jq -r '.input_tokens // 0')
out=$(printf '%s' "$usage"  | jq -r '.output_tokens // 0')
cin=$(printf '%s' "$usage"  | jq -r '.cache_read_input_tokens // 0')

cost=$(jq -n --argjson i "$in" --argjson o "$out" --argjson c "$cin" \
  '($i*3 + $o*15 + $c*0.3)/1000000')

# 追加一行 JSONL 到账本
jq -c -n \
  --arg ts "$(date -u +%FT%TZ)" \
  --arg sid "$session_id" \
  --argjson cost "$cost" \
  --argjson in "$in" --argjson out "$out" \
  '{ts:$ts, session_id:$sid, input_tokens:$in, output_tokens:$out, cost_usd:$cost}' \
  >> .claude/cost-ledger.jsonl

exit 0   # exit 0 放行;exit 2 会拦截并显示 stderr

跑几篇笔记后,.claude/cost-ledger.jsonl 里会留下一条条记录:

{"ts":"2026-07-06T08:21:03Z","session_id":"abc…","input_tokens":1820,"output_tokens":240,"cost_usd":0.0088}

一键看总账:

jq -s '{runs: length, total_cost: (map(.cost_usd)|add), total_in: (map(.input_tokens)|add)}' .claude/cost-ledger.jsonl

📌 hook 退出码约定:0 成功放行;2 阻断并把 stderr 展示给 Claude(可用于"输出不合规则强制重试");其他非零码是非阻塞错误(记日志但不打断)。

常见问题:

八、实战案例:Markdown 笔记索引管道(完整)

把以上拼成一个可运行的工程。目录结构:

notes-indexer/
├── notes/                  # 你的 Markdown 笔记
│   ├── 2026-07-01-plan.md
│   └── 2026-07-02-rag.md
├── schemas/
│   └── note-card.json
├── .claude/
│   ├── settings.json       # Stop hook 注册
│   ├── cost-ledger.sh
│   └── cost-ledger.jsonl   # 自动生成
└── extract-notes.sh        # 主脚本

extract-notes.sh(双轨:既写卡片,又就地记账,不依赖 hook):

#!/usr/bin/env bash
set -euo pipefail

SCHEMA="schemas/note-card.json"
OUT="cards.jsonl"
: > "$OUT"   # 清空

process_one() {
  local f="$1"
  local resp
  resp=$(cat "$f" | claude -p "你是笔记编目员。提取标题、一句话摘要、3-5个标签、核心技术。忠于原文,不要编造。" \
            --output-format json \
            --allowedTools "Read" \
            --json-schema "$(cat "$SCHEMA")")

  # 取结构化结果 + 来源文件 + 本次成本,合成一行
  jq -c --arg src "$f" '
    .structured_output
    + {source: $src, cost_usd: .total_cost_usd, session_id: .session_id}
  ' <<<"$resp"
}

# 并发处理,结果汇总
export -f process_one
export SCHEMA
find notes -name '*.md' -print0 \
  | xargs -0 -n1 -P4 -I{} bash -c 'process_one "$1"' _ {} >> "$OUT" || true

# 聚合成索引
jq -s '{generated_at: (now|todate), count: length,
        total_cost: (map(.cost_usd//0)|add), notes: .}' "$OUT" > index.json

echo "✓ 已生成 index.json($(jq '.count' index.json) 篇,约 \$$(jq '.total_cost' index.json))"

运行:

chmod +x extract-notes.sh
./extract-notes.sh

得到的 index.json 片段:

{
  "generated_at": "2026-07-06T08:31:00Z",
  "count": 2,
  "total_cost": 0.0176,
  "notes": [
    {
      "title": "七月技术规划",
      "summary": "梳理本月要在后端落地的 RAG 与可观测性改造。",
      "tags": ["规划", "RAG", "监控"],
      "tech": ["FastAPI", "Postgres", "OpenTelemetry"],
      "source": "notes/2026-07-01-plan.md",
      "cost_usd": 0.0088
    }
  ]
}

这一份 index.json 可以直接喂给任意静态站点生成器做全文搜索、标签云、技术雷达。而 .claude/cost-ledger.jsonl 在你交互式调试时默默记下每一笔开销。两条可观测路径互补:脚本内读 total_cost_usd(bare 也能用),hook 读 transcript(非 bare 才触发)。

九、工作流程图

flowchart LR
    A[notes/*.md] -->|cat 管道| B[claude -p]
    S[schemas/note-card.json] -->|--json-schema| B
    B -->|--output-format json| C[结构化响应]
    C -->|jq .structured_output| D[cards.jsonl]
    C -->|.total_cost_usd| E[成本字段]
    D -->|jq -s 聚合| F[index.json]
    E --> F
    B -.会话结束.-> H[Stop hook]
    T[transcript.jsonl] --> H
    H -->|算 token| L[cost-ledger.jsonl]
    style B fill:#fef3c7,stroke:#b45309
    style F fill:#dcfce7,stroke:#15803d
    style L fill:#dbeafe,stroke:#1d4ed8

十、常见问题与排错

十一、延伸阅读

十二、训练营适用性评估

参考来源

  1. Run Claude Code programmatically — Claude Code Docs — 官方 -p/--bare/--output-format/--json-schema 权威文档,本文命令与字段均出自此处
  2. Hooks reference — Claude Code Docs — hooks 事件、配置 schema、JSON 输入输出与退出码约定的官方参考
  3. Automate actions with hooks — Claude Code Docs — hooks 快速上手与示例,Stop/UserPromptSubmit 等
  4. Claude Code Headless Mode: The Definitive Guide — claude -p 的 jq 解析、stdin 管道、实战模式第三方详解
  5. Headless Mode and CI/CD Tutorial — SFEIR Institute — 把 headless 模式接入 CI/CD 流水线的实战教程

[!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学通信知识星球海报-扫码加入