DOCAI · 课程 · L3 · 24 分钟 · 命令参考
`cat file | claude -p` — 处理管道内容
把日志、diff、接口返回直接喂给 claude -p:标准输入与提示词如何拼接、JSON 结果怎么接、退出码和权限在脚本里怎么管。
你将学会
- 能把日志、diff、接口返回通过管道交给 claude -p 处理
- 能写清提示词与素材的边界,避免它抓错重点
- 能在脚本里判断退出码、控制权限并记录花费
一条管道能省掉多少复制粘贴
线上日志几万行,你只想知道哪几类报错最该先修。改完代码,你想在提交前听一遍不同意见。接口返回一大段数据,你想要一句人话总结。这些素材本来就在文件或命令输出里,复制进对话框既慢,也容易漏掉关键行。
claude -p 是单次执行模式:给出任务,拿到结果,进程结束,不进入交互界面。它同时读取标准输入,所以 shell 的管道可以直接把内容送进来。
grep -i "error" logs/app.log | claude -p "按出现频率归类这些报错,指出最该先修的三类"
左边筛选,右边判断。整节课讲的就是这条分工线。
它收到的到底是什么
管道内容不是附件,而是和你写在命令里的那句话拼成同一段输入。两件事因此要交代清楚:
- 指令要具体:看什么、按什么标准排、输出几句、用什么语言。
- 素材要说明:这是哪个时间段的日志、哪些行可以忽略、字段含义是什么。
素材里夹着代码或表格时,加一行分隔,让它知道正文从哪开始。内容太长就先裁剪:只取相关时段、只留报错行、去掉重复堆栈。省下的 token 是实打实的成本,更能减少它被无关内容带偏的机会。上下文如何计量,见上下文窗口。
三个马上能用的组合
审查改动。 diff 直接进管道,提示词只写关注点:
git diff | claude -p "只报会改变运行结果的问题,按文件分组;格式和命名不要提"
整理提交记录。 让它输出纯文本,直接贴进发布说明:
git log --oneline v1.2..HEAD | claude -p "按新增、修复、其他分三段整理,不要补充记录里没有的内容"
读接口数据。 先取数,再让它挑异常:
curl -sS https://example.com/api/stats | claude -p "找出偏离日常范围的指标,各用一句话解释可能原因"
三个例子的结构一致:命令负责取数,提示词负责定性,输出约束成你要的形状。路径和地址都要换成你自己的;先拿一小段样本验证提示词,确认输出可用,再放到全量数据上跑。
让结果能被脚本接住
给人看的回答是段落,给程序用的回答要结构化。加 --output-format json,再从中取正文。返回体里除了 result,通常还有 is_error、total_cost_usd、duration_ms 这类字段;字段名随版本变化,写解析前先打印一份看结构。
report=$(grep -i "error" logs/app.log | claude -p "归并同类报错,按影响排序" --output-format json)
printf '%s\n' "$report" | jq -r '.result'
两条原则:先看 is_error、再校验收到的结构,最后才取 result;解析失败按错误处理,不要静默变成空字符串。需要边生成边处理时,可以选流式输出,把逐条事件交给下游;只是落盘的话,一次性结果更省心。
脚本里的四条纪律
- 看退出码。 成功为零,失败非零。紧跟判断,或在脚本开头设置遇错即停。
- 设超时。 外层用
timeout包住,别让一次卡住的调用拖死整条流水线。 - 管权限。 单次模式不等于放开写权限;改文件、跑命令仍受权限设置约束。无人值守时先缩小工作范围,再决定放到多宽。
- 记账。 每次调用的耗时与费用写进日志;跑批量前先用一条样本估算总量。
批量处理时逐条循环、逐条检查返回值。前一条失败,就不要继续覆盖后面的产物。要重试就限次数、拉长间隔,别在故障时空转。
一个最小可用的检查脚本
#!/usr/bin/env bash
set -euo pipefail
log="logs/app.log"
out=$(grep -i "error" "$log" | claude -p "归并同类报错,给出三条最优先的修复建议")
printf '%s\n' "$out"
注意 grep 没匹配到内容时退出码非零,配合 set -o pipefail 会让脚本提前结束;要么在 grep 后加 || true,要么把「没有报错」当成正常分支,先判断匹配结果再决定是否调用。
先在只读场景把它跑顺,再考虑加入写文件、发通知这些动作。判断标准很朴素:单独跑十次,结果稳定、退出码可信,才值得固化。
一次处理一批文件:完整脚本
单条命令能跑通,不代表批量任务安全。下面这个脚本处理一个目录里的 Markdown 文件,每个文件单独调用一次,产物写入 out/,任何一份失败都不会拖垮其余文件:
#!/usr/bin/env bash
set -uo pipefail
in_dir="docs"
out_dir="out"
mkdir -p "$out_dir"
failed=0
for f in "$in_dir"/*.md; do
[ -e "$f" ] || { echo "没有匹配到文件" >&2; exit 1; }
name=$(basename "$f" .md)
tmp="$out_dir/$name.txt.part"
if claude -p "只输出三条要点,每条一行,不加标题和前言" \
< "$f" > "$tmp"; then
mv "$tmp" "$out_dir/$name.txt"
echo "完成: $f"
else
rm -f "$tmp"
echo "失败: $f" >&2
failed=1
fi
done
exit "$failed"
几个细节值得照搬:
- 先写临时文件,成功后再改名,避免下游读到半截结果。
- 失败计数而不是立刻退出,批处理时你希望知道总共有几份没成功。
- 没有匹配到文件时明确报错;否则循环体一次都不执行,脚本会「成功」退出。
- 产物目录与源目录分开,重复运行也不会覆盖原文。
跑之前先拿一份样本试提示词:
claude -p "只输出三条要点,每条一行,不加标题和前言" < docs/示例.md
确认输出形状稳定,再把同一句话放进循环。若每份文件需要不同的指令,就在循环里按文件名分支,不要把所有规则塞进一条超长提示词。
常见故障:现象、原因与处理
| 现象 | 为什么 | 怎么办 |
|---|---|---|
| 管道左边没输出,右边仍返回一段泛泛而谈 | 上游命令没匹配到内容,管道默认不传递失败状态 | 先看上游退出码与行数;空输入时直接跳过调用 |
| 提示词的要求被素材淹没 | 标准输入与命令行文字拼成同一段,长素材占了大部分注意力 | 提示词写具体约束,素材前后加分隔行,内容太长先裁剪 |
| JSON 解析报错或取到空值 | 输出里混入解释文字,或字段名与当前版本不同 | 用 JSON 输出模式,先打印整体结构再取字段,并检查错误标记 |
| 脚本在无人值守时停住 | 某一步需要人工确认权限,单次模式也不会自动同意 | 只读任务先跑;确需写操作时缩小目录范围,并按当前版本配置权限 |
| 跑了一夜,账单超出预期 | 循环次数乘以单次费用,没有先估算 | 先用一份样本记录耗时和费用,再乘总数;日志里保留每次的用量字段,估算方法见费用估算 |
| 长日志让回答只覆盖开头和结尾 | 输入超过上下文容量,中段被挤出 | 用 head、grep 或时间窗口先缩小范围,分段处理再汇总 |
还有三类任务不适合走管道。需要来回追问、逐步改稿的,应该用交互模式;需要对同一个仓库连续做多处修改的,单次调用每次都要重新建立上下文,效率反而低;材料里含密钥、客户隐私或未脱敏数据的,不要直接送进命令行,先脱敏或改用受控环境。
下一节处理另一半问题:任务做到一半,怎么接着上次的上下文继续,见继续最近的对话。
自测
先自己回答,再展开对照;答不上来就回到对应小节。01管道内容会替换掉命令行里的提示词吗?看答案
不会。标准输入和 -p 后面的那句话会拼成同一段输入,所以指令要写全,素材的范围和含义也要交代。
02为什么在脚本里调用时要先看退出码?看答案
成功返回零、失败返回非零,这是脚本分流的唯一可靠依据;只看输出文本会把失败当成空结果继续往下跑。