CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Claude Code 实战入门/课时/Hooks 事件
RawBlame
课时目录12 节

Claude Code 实战入门

  1. 01Claude Code 概览
  2. 02工作流与 Plan 模式
  3. 03CLI 与会话
  4. 04模型与定价
  5. 05上下文与 CLAUDE.md
  6. 06权限与安全
  7. 07MCP 与插件
  8. 08Skills 与子代理
  9. 09Hooks 事件
  10. 10扩展与规模化
  11. 11内置工具集
  12. 12综合练习

Hooks 事件

Pre/Post tool 与 session 钩子。

30 分钟

Hooks 事件

这一课你将能做到

  1. 说出 PreToolUse / PostToolUse / UserPromptSubmit / Stop 等核心事件的触发时机与用途
  2. 在 settings.json 中配置带 matcher 的 Hook,实现「编辑后自动格式化」「危险命令硬拦截」
  3. 遵守退出码协议(0 放行 / 2 阻止)编写跨平台的检查脚本,并用 /hooks 验证加载

为什么要学

CLAUDE.md 是「拜托它记住」,Hooks 是「制度上必须」。模型有概率忘记规范,但 Hook 是外挂的 shell 脚本,100% 确定:编辑器保存后自动跑格式化,就算模型忘了也给你补上;它想执行 rm -rf,PreToolUse 直接把工具调用掐断。官方定位很明确——凡是「必须发生 / 必须不发生」的规则,都该用 Hooks 落地而不是靠提示词。这是把 Claude Code 接入工程化团队的关键一环,也是面试里区分「用过」和「用透」的考点。

人话:Hooks 是给 agent 装的门禁和流水线质检仪。它每做一个动作(跑命令、改文件),先过你设的卡口;卡口放行才继续,卡口喊停它就得停下听原因。

先建立图像

一次 Edit 操作的完整过卡流程:

Claude 决定调用 Edit(order.java)
      ↓
PreToolUse (matcher: Edit)     ← 执行前:可拦截、可放行
      ↓ 放行
Edit 真正执行,文件被修改
      ↓
PostToolUse (matcher: Edit)    ← 执行后:可格式化、可提醒
      ↓
Claude 看到工具结果,继续思考

其他事件:
UserPromptSubmit ← 你提交输入时(可注入上下文/拦截)
Stop             ← Claude 完成回答时(可触发通知/收尾检查)
PreCompact       ← 上下文压缩前
SessionStart/End ← 会话开始/结束

配置结构:

{
  "hooks": {
    "事件名": [
      { "matcher": "工具名1|工具名2",
        "hooks": [ { "type": "command", "command": "你的脚本" } ] }
    ]
  }
}

跟着做

步骤 1:用 /hooks 认识配置入口

/hooks

交互菜单按事件分组展示当前配置,也能安全地新增(改完需确认,防恶意配置注入)。先只读一遍,理解「事件 → matcher → 命令」三层结构。

步骤 2:实现「编辑后自动格式化」(PostToolUse)

编辑 .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\" 2>/dev/null || true" }
        ]
      }
    ]
  }
}

此后每次 Claude 保存文件,prettier 自动跟进。脚本通过 stdin 收到 JSON(含 tool_name、tool_input 等),文件路径也可从 $CLAUDE_FILE_PATHS 读取。

步骤 3:实现「危险命令硬拦截」(PreToolUse)

写一个跨平台检查脚本 scripts/guard.sh:

#!/bin/bash
input=$(cat)                     # 从 stdin 读取 JSON
cmd=$(echo "$input" | jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -qE 'rm -rf|git push --force|drop database'; then
  echo "拦截:包含高危命令 [$cmd]" >&2   # stderr 在 exit 2 时反馈给模型
  exit 2                                # 非零且为 2:阻止本次工具调用
fi
exit 0                                  # 放行

配置挂载:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash",
        "hooks": [ { "type": "command", "command": "bash scripts/guard.sh" } ] }
    ]
  }
}

重启会话后,让它「删除 node_modules 重建」试试:它执行 rm -rf node_modules 会被拦下并收到你的 stderr 消息,通常它会改用更安全的命令。

步骤 4:体验「完成通知」(Stop / Notification)

{
  "hooks": {
    "Stop": [
      { "hooks": [ { "type": "command", "command": "echo 任务完成 | terminal-notifier -title 'Claude Code' 2>/dev/null || true" } ] }
    ]
  }
}

长任务跑着去干别的,完成后系统弹通知。Notification 事件则在「Claude 需要你的注意」(如等权限确认)时触发,二者配合消灭「干等」。

步骤 5:调试与验证

claude --debug    # 启动时显示 hook 执行细节

会话内 /hooks 确认配置已加载;脚本加日志(追加到本地文件)是最朴素的排障手段。

讲清楚要点

  1. 退出码即协议:exit 0 = 成功放行(stdout 可在部分场景注入上下文);exit 2 = 阻止(stderr 内容反馈给模型,让它知道为什么被拦);其他非零 = 非阻塞错误,流程继续。
  2. matcher 只作用于工具类事件:PreToolUse / PostToolUse 按 Edit|Write|Bash 这类工具名过滤;UserPromptSubmit、Stop 等没有 matcher,对所有情况触发。
  3. 脚本能拿到结构化上下文:stdin 的 JSON 含 session_id、tool_name、tool_input(命令、文件路径、参数)等,足够写精细策略(按分支、按路径、按时间段拦截)。
  4. Hook 是同步执行的:卡在哪个工具调用上,会话就等在那里。脚本必须快(官方有超时上限,可按 hook 配 timeout),格式化、检查类适合,构建、部署类不适合。
  5. Hooks 配置可被会话加载时审核:通过 /hooks 修改会要求确认;直接改 settings.json 后需重启会话生效——防止恶意仓库借配置投毒,也是团队分发时要注意的安全点。

你可能会懵的地方

  • 有了 Hooks 还需要权限 deny 规则吗? 需要,分层使用。deny 规则零成本、覆盖常规黑名单;Hooks 处理需要逻辑判断的策略(扫描命令内容、检查文件内容、看环境)。简单场景优先规则,复杂策略才上脚本。

  • PostToolUse 里格式化失败会怎样? 非阻塞。工具已经执行完,Hook 的失败不会撤销修改,只会作为信息返回。所以格式化类 hook 建议容错(|| true),别让格式化工具没装卡死正常流程。

  • stderr 和 stdout 到底哪个有用? 看目的:要阻止并告知原因 → exit 2 + stderr;要注入参考信息(如目录结构)→ 部分事件支持 stdout 作为上下文。记不准就查官方 hooks 文档的输入输出表。

  • Hook 脚本写错了会卡死会话吗? 有超时机制,超时按失败处理,不会永久卡死。但会白白等待,所以脚本里任何外部调用都要加超时与容错。

  • Windows 上怎么写? 原理相同,命令字段用 PowerShell 或调用跨平台脚本(Node/Python)。注意编码与换行符(脚本文件用 LF),路径用 $env: 形式或统一从 stdin JSON 取。

错法对照

错法问题正解
在 Hook 里跑构建/部署每次工具调用都被拖慢数十秒只放轻量检查与格式化
PreToolUse 配了 matcher: "*" 还想拦 MCP匹配面过宽且 MCP 工具名不同matcher 精确到工具名,含 mcp__ 前缀
用 exit 1 想阻止操作1 是非阻塞错误,照样放行阻止必须 exit 2,原因写 stderr
脚本无容错,工具未装就崩每次调用都报错刷屏try/或
敏感逻辑(扫描密钥)只写在提示词里模型可能忽略用 PreToolUse 硬拦截,提示词作辅助

卡点急救

  1. Hook 完全没执行:/hooks 查看是否加载;settings.json 语法是否合法;确认重启过会话;claude --debug 看执行日志。
  2. 误拦截正常操作:放宽脚本条件或 matcher 范围;在脚本里加白名单分支;保留日志定位误判样本。
  3. 会话明显变卡:排查 Hook 是否在做重活(网络请求、全量扫描);把重逻辑移出,或缩短超时让它快速失败。

面试常问

  • Hooks 与权限系统、CLAUDE.md 三者如何配合? CLAUDE.md 软引导(模型倾向),权限规则静态硬约束(黑白名单),Hooks 动态硬约束(任意脚本逻辑)。规范类靠引导,常规禁令靠规则,需要上下文判断或执行副作用(格式化、通知)的场景靠 Hooks,构成纵深防御。

  • 设计一个「防止提交密钥」的方案? 多层:CLAUDE.md 写明密钥规范;PreToolUse(Hook) 对 Edit/Write 的 tool_input 做模式扫描,命中密钥特征 exit 2;PostToolUse 对被改文件跑检测工具;CI 侧再兜底扫描。教育(记忆)+ 拦截(钩子)+ 审计(CI)三线并行。

  • 为什么 Hooks 选择「退出码 + stdin JSON」这种朴素协议? 零依赖、语言无关、可与任意 Unix 工具链组合,CI 与本地行为一致;结构化输入(JSON)保证可编程性,退出码保证语义简单可靠。这是典型的 Unix 哲学接口设计。

自测三问

  1. PreToolUse 与 PostToolUse 分别适合放什么逻辑?举两例。
  2. exit 0、exit 2、其他非零分别是什么语义?
  3. 为什么不能把「跑一次完整构建」放进 PostToolUse?

小结一句话

Hooks 用「事件 + matcher + 脚本 + 退出码」把规范从提示词升级为机制:轻逻辑放前后钩子,重流程交给 CI,退出码协议是唯一的约定语言。

延伸

  • Hooks 官方文档
  • 下一课:扩展与规模化——headless 模式、CI 集成与多智能体协作,把 Claude Code 从个人工具变成团队基建。
加载本节练习…
← 上一课Skills 与子代理下一课 →扩展与规模化