GuidesAI 编程
用 Claude Code Hooks 拦截关键操作
在 settings.json 里配置 PreToolUse 等 hooks,在工具执行前拦截危险命令或保护敏感文件。
基于 Claude Code Docs 整理 · 官方资料 ↗
权限规则适合声明“允许 / 询问 / 拒绝”哪些工具。Hooks 则更进一步:在 Claude Code 生命周期的确定点,自动跑你的命令或脚本,用确定性逻辑拦截、审计或补充上下文。
官方 Hooks reference 列出全部事件;入门最有用的是 PreToolUse——在工具真正执行前触发,可以阻断。配套的 Hooks guide 提供了通知、格式化、保护文件等现成例子。
这篇先注册一个可验证的 PreToolUse 拦截,再说明 matcher、exit code 与 settings 放哪里。
Hooks 解决什么问题
Hooks 是用户定义的 shell 命令、HTTP 端点或 LLM prompt,在会话生命周期固定点执行。终端、IDE、Desktop 与 Claude Code on the web 都会触发同一套事件。
常见节奏:SessionStart / SessionEnd 每会话一次;UserPromptSubmit / Stop 每轮一次;PreToolUse / PostToolUse 在 agent 循环的每次工具调用时触发。要拦截危险操作,优先 PreToolUse。
与只靠 CLAUDE.md 叮嘱不同,hook 是强制执行的:匹配到就跑你的脚本,不依赖模型是否“想起来”遵守规则。
配置写在哪里
在 settings 的 hooks 字段注册。常用位置:
- ~/.claude/settings.json:对本机所有项目生效
- .claude/settings.json:仅当前项目,可提交仓库共享
- .claude/settings.local.json:本机个人覆盖(注意勿提交密钥)
用会话内 /hooks 浏览已注册事件(只读)。新增或修改需改 JSON,或让 Claude 帮你改 settings。
用 PreToolUse 拦截危险 Bash
官方示例用 matcher: "Bash",再配合 if: "Bash(rm *)" 收窄到删除类命令。脚本从 stdin 读 JSON,若命令含 rm -rf,则返回 permissionDecision: "deny"。
创建拦截脚本并赋予可执行权限
mkdir -p .claude/hooks
cat > .claude/hooks/block-rm.sh <<'EOF'
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0
fi
EOF
chmod +x .claude/hooks/block-rm.sh写入 .claude/settings.json 的 hooks
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}解析 JSON 依赖 jq,需已安装并在 PATH 中。改完 settings 后重新进入项目会话,再让 Claude 执行含 rm -rf 的命令验证是否被拦。
保护 .env 等敏感文件
Hooks guide 的另一常见模式:在 Edit|Write 前检查 file_path,命中 .env、package-lock.json、.git/ 等模式就 exit 2 阻断。
protect-files.sh(官方 guide 思路)
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
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 0PreToolUse matcher: Edit|Write
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}exit 2 会阻断工具调用,并把 stderr 反馈给 Claude。exit 0 且不输出 deny 决策时,仍走正常权限流程。
stdin / exit code 怎么用
事件触发时,Claude Code 把 JSON 喂给命令 stdin。PreToolUse 常见字段包括 hook_event_name、tool_name、tool_input(Bash 时含 command)。
官方示例:Bash npm test 的 hook 输入形态
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}- exit 0:不通过 exit code 表示反对;可配合 JSON permissionDecision
- exit 2:阻断(PreToolUse 会阻止工具调用)
- 多个 hook 同时匹配时都会跑完,再按最严格决策合并(deny > ask > allow)
怎么确认 hook 生效
- 会话内 /hooks 能看到对应事件与命令
- 故意触发 rm -rf 或编辑 .env 时被阻断
- 阻断原因出现在反馈里(JSON reason 或 stderr)
- 脚本路径错误时会出现非阻塞失败提示——策略型 hook 首次运行要盯住这个
需要完整 schema、HTTP hooks、async hooks 时,回到 Hooks reference。日常自动化示例优先看 Hooks guide。
最容易踩的坑
hook 从未运行
检查 settings 路径与 JSON 是否合法;确认 matcher 匹配工具名;用 /hooks 核对是否加载;脚本需 chmod +x。
jq: command not found
安装 jq(macOS: brew install jq)。官方 Bash 示例依赖它解析 stdin JSON。
脚本失败但危险命令仍执行
许多事件上,hook 启动失败是非阻塞的。策略 hook 首次一定要确认路径可执行,不要假设“写了 JSON 就生效”。
想拦截却用了 PostToolUse
PostToolUse 在成功之后。要阻止执行,必须用 PreToolUse(或相应决策事件)。
官方资料
Claude Code Docs
Hooks reference ↗