跳到正文
docai.cc

DOCAI · 课程 · L3 · 19 分钟 · 命令参考

`claude -p`:单次查询模式

没有人坐在终端前的时候,用 -p 让它在一次运行里读完、做完、退出。这一节讲管道输入、可被脚本解析的输出格式,以及权限与超时该怎么设。

Claude Code 程序员 · CLI 命令详解

你将学会

  • 能把 -p 模式接进管道和定时任务
  • 能用 json 或 stream-json 输出配合脚本解析
  • 能给无人值守任务设置权限边界与失败处理

没有人坐在终端前

定时巡检、提交前检查、合并请求里的自动评论——这些任务有一个共同点:跑的时候你不在。它们不能停在确认框上等一个永远不会来的回车。

-p(也可写作 --print)就是为这种场合准备的。它执行一轮任务,把结果写到标准输出,然后退出,不进入对话界面。

最小可用形态

claude -p "把 README 的英文摘要翻成中文" > summary.txt
echo $?

结果落在文件里,退出码留给脚本判断:成功为零,失败非零。别把两者混在一起输出,否则下游没法区分「结果为空」和「根本没有结果」。

这也是它和交互模式最本质的区别:没有下一步,只有这一次。每次调用都是独立进程,上一轮的上下文不会自动带进来。

从标准输入拿内容

管道是它最常被低估的用法:

git diff --staged | claude -p "总结这次改动,输出三条以内的要点"
cat error.log | claude -p "找出最可能的根因,并按可能性排序"

数据从标准输入进来,提示词负责说明「这是什么、要交回什么格式」。只说「分析一下」,它可能给你一篇散文;说清「输出三行,每行一个结论」,结果才能进下一道工序。

注意输入长度受上下文窗口限制。日志动辄几十兆,先 grep、tail 或按时间切片,再交给它,比整份丢进去更可靠。

输出得让机器读得懂

格式 形态 用法
text 纯文本,人读 日志、终端展示
json 结束后一个完整对象 脚本取字段
stream-json 一行一个事件,持续输出 实时消费、接入前端
claude -p "检查 src/auth.ts 的安全问题,用 JSON 说明每条风险" \
  --output-format json \
  < src/auth.ts > result.json

jq -r '.result' result.json

字段名与结构以当前版本为准,升级后先拿一条样本验证再改脚本。要边跑边处理,就换成 stream-json,按行读取事件;注意 --print 搭配 stream-json 时必须同时加 --verbose,否则命令会直接报错退出。事件类型同样以官方文档为准,流式的机制见流式输出。

权限:没人按「允许」

这是 -p 最容易踩的坑。默认情况下,未被允许的写操作会被拒绝,模型收到拒绝结果后往往回你一段「建议这样改」。脚本看起来成功退出,文件却纹丝不动。

正确做法是把权限收窄到刚好够用:

claude -p "给 src/utils.ts 的 parseDate 补单元测试" \
  --allowedTools "Read" "Edit" "Bash(npm test *)" \
  --max-turns 8

只放行这个任务需要的工具,比一次性全开安全得多。--dangerously-skip-permissions 适合一次性容器或隔离环境,不要放进长期运行的流水线。权限判断在执行前的哪个位置,见命令执行流程。

还有一个细节:放行的工具越精确,脚本越稳定。写成 Bash(git diff *) 这样的范围,模型不会顺手跑别的命令;只写 Bash,它能做的事就多得多。升级版本后回来检查一次允许列表,别让旧规则默默放宽或失效。

超时、重试与失败信号

无人值守时,最怕的是「卡住」和「失败了却看不出来」。两层保护就够:

timeout 120 claude -p "检查构建日志,列出会阻塞发布的错误" \
  --allowedTools "Read" "Bash(grep *)" \
  --max-turns 4 < build.log

外层 timeout 负责兜底(macOS 上可能需要 coreutils 提供的 gtimeout),--max-turns 防止它在工具之间来回打转。重试只对临时故障有意义,比如网络抖动、限流;权限被拒、输入为空这类问题,重试三次结果一样。可以这样写:

ok=0
for attempt in 1 2 3; do
  if claude -p "审查暂存区改动,只报会出错的地方" \
    --allowedTools "Read" "Bash(git diff *)" \
    --output-format json > review.json; then
    ok=1
    break
  fi
  echo "第 ${attempt} 次失败" >&2
  sleep $((attempt * 5))
done
[ "$ok" = 1 ] || { echo "三次均失败,保留日志" >&2; exit 1; }

判断失败不要只看输出是否为空:空输出可能是真的没有问题,也可能是进程被杀。以退出码为准,配合 set -o pipefail,管道里任何一段失败都能传出来。重试间隔逐次拉长的做法,见退避重试。

放进脚本前的检查清单

  • 引号。 提示词整句包住,含反引号时用单引号。
  • 轮数上限。 用 --max-turns 兜底,防止来回打转。
  • 输出格式。 要完整结果用 json,要实时反馈用 stream-json。
  • 失败处理。 检查退出码,别让失败被管道吞掉。
  • 时间和费用。 给外层命令设超时,给调用频次设上限;重复度高的任务可以看看成本估算。
  • 样本验证。 先手动跑一条,确认输出结构,再批量。
#!/usr/bin/env bash
set -euo pipefail

report=$(claude -p "审查暂存区的改动,只列会导致故障的问题" \
  --allowedTools "Read" "Grep" "Bash(git diff *)" \
  --max-turns 6 \
  --output-format json)

echo "$report" | jq -r '.result'

失败时脚本会因非零退出码停下,而不是把空字符串当成「没有问题」。

-p 把对话变成一次函数调用:输入明确、输出可解析、成败有退出码。它和 Agent SDK 里的单次调用是同一类模式,这里练熟的参数,写进程序时概念相通。想理解每次调用消耗在哪里,可以看Token。下一节专门看管道这条线——处理管道内容。

自测

先自己回答,再展开对照;答不上来就回到对应小节。
  1. 01为什么 -p 模式在脚本里常常「什么都不改」就结束了?看答案

    无人确认权限,未被允许的写工具被拒绝;需要提前用允许列表放行必要的工具。

  2. 02脚本里要拿完整结果,选哪种输出格式?看答案

    选 json,等任务结束后输出一个完整对象;需要边跑边处理时选 stream-json。

查看 Markdown

涉及的概念

在知识树上