这篇适合谁、读完能做到什么
想让某些事情每次都发生、或者绝对不发生的人。读完你应该能在 settings.json 里配好一个桌面通知,给编辑后的文件自动格式化,拦住对敏感文件的修改,并知道 hook 没生效时该怎么查。[1]
hook 和 CLAUDE.md 有什么区别
Hook 是你写的 shell 命令,Claude Code 在特定的生命周期节点运行它。好处是确定性:某件事一定会发生,而不是指望模型记得去做。文档的说法是,hook 适合执行项目规则、自动化重复操作。[1] 需要判断力的情况,可以改用基于提示或基于代理的 hook,由 Claude 模型来评估条件。[1]
第一个 hook:Claude 等输入时发通知
最简单的例子是在 ~/.claude/settings.json 里加一个 Notification hook,让 macOS 在 Claude 等待输入时弹出通知。命令是 osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'。[1] 如果文件里已经有 hooks 字段,就把 Notification 作为同级的事件键加进去,而不是替换整个对象。[1]
配好后在 Claude Code 里输入 /hooks,可以打开 hooks 浏览器,确认新 hook 出现在对应的事件下面。[1]
编辑文件后怎么自动格式化
在 .claude/settings.json 里配一个 PostToolUse hook,matcher 写 Edit|Write,只有编辑文件的工具运行后才触发。命令是:[1]
f=$(jq -r '.tool_input.file_path') && npx prettier --write --ignore-unknown "$f"hook 成功运行时,对话里什么都不会显示。要确认它生效,就看被编辑的文件是否已经被重新格式化。[1] 如果想不管文件被谁改过,包括 Bash 命令改写的文件,文档建议改用 FileChanged hook。[1] 文档中的 Bash 示例都用 jq 解析 JSON,macOS 上可以用 brew install jq 安装。[1]
怎么拦住对 .env 的修改
这个例子用一个脚本实现。把下面的内容保存为 .claude/hooks/protect-files.sh:[1]
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0脚本要有执行权限。在 macOS 和 Linux 上运行 chmod +x .claude/hooks/protect-files.sh。[1] 然后注册一个 PreToolUse hook,matcher 为 Edit|Write,命令是 "$CLAUDE_PROJECT_DIR"/.claude/hooks/protect-files.sh。[1] 测试的办法是让 Claude 给 .env 加一行注释。Claude Code 会在编辑前拦下它,并把脚本输出的 Blocked: 信息作为反馈交给 Claude。[1]
hook 不生效怎么查
- hook 配了但从不执行:先用
/hooks确认它挂在正确的事件下。matcher 区分大小写,PreToolUse在工具执行前触发,PostToolUse在执行后触发。[1] - 报 hook error:手动用样例 JSON 管道喂给脚本,再看退出码,例如
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh,然后运行echo $?。[1] /hooks里什么都没有:检查 JSON 是否合法,尾随逗号和注释都不允许。项目级 hook 写在.claude/settings.json,全局 hook 写在~/.claude/settings.json。[1]- 拼 JSON 输出时出现解析错误:用
jq这类 JSON 编码工具生成内容,不要用字符串拼接,这样值里的引号和反斜杠会被正确转义。[1]
hook 容易踩的坑有哪些
PostToolUse 无法撤销已经执行的动作,因为工具已经跑完了,要拦截只能用 PreToolUse。[1]
Stop hook 在 Claude 每次结束回复时都会触发,不只是任务完成时,而且用户打断时不触发。如果它连续拦截八次且中间没有工具调用,Claude Code 会强制放行。文档建议 hook 读取输入里的 stop_hook_active 字段,如果已经触发过续接,就直接退出。[1]
hook 不能突破权限规则。返回 permissionDecision: "deny" 的 hook 即使在 bypassPermissions 模式下也会拦住工具调用,但 hook 返回 allow 不会覆盖设置里的拒绝规则。[1]
同一事件下多个 hook 都会匹配时,所有命令都会跑完,再由 Claude Code 合并结果。对 PreToolUse 的权限决定,取最严格的那个,顺序是 deny、defer、ask、allow。[1]
命令 hook 只通过标准输出、标准错误和退出码与 Claude Code 通信,不能触发斜杠命令或工具调用。默认超时是十分钟,可以用 timeout 字段按秒覆盖。[1]
下一步
先从通知或格式化这样的简单 hook 开始,确认它按预期运行,再加拦截类的规则。完整的事件列表和 JSON 格式见官方的 hooks 参考页面。[1]
来源
- 1Automate actions with hooksAnthropic Claude Code docs
引用本页: Claude Code Hooks 实战:自动格式化、拦截危险命令. TellWorld, https://www.tellworld.net/zh/practice/practice-claude-code-hooks. 更新于 2026-11-14