Hooks类型详解:before/after触发机制

理解before和after两种Hook

一、before Hook详解

before Hook 在事件执行之前触发,是 Claude Code Hooks 系统中最重要的控制节点。它的核心价值在于能够在事件发生前进行验证、检查和审批,从而实现对工作流的精确控制。

1.1 执行时序

before Hook 的执行时机位于"用户触发事件"和"事件实际执行"之间。当一个事件被触发时,系统会先检查是否存在对应的 before Hook,如果存在则先执行 Hook 脚本,根据 Hook 的退出码决定是否继续执行后续事件。

执行流程: 用户操作 → 触发事件 → 检查before Hook → 执行Hook脚本 → 退出码为0 → 继续执行事件 → 退出码非0 → 阻断事件,返回错误信息

1.2 核心特性

1.3 典型应用场景

代码质量门禁
在 git commit 前运行 linter 和测试,代码质量不达标则阻止提交。
权限检查
在执行敏感操作前验证用户权限,防止未授权的操作。
环境准备
在任务执行前自动拉取最新代码、安装依赖、检查磁盘空间。
配置校验
验证配置文件格式是否正确、必要配置项是否完整。

1.4 超时和失败处理

before Hook 支持超时机制。如果 Hook 脚本在指定时间内未完成执行,系统会将其视为超时失败,并按照非零退出码的方式处理——即阻断事件执行。默认超时时间可以通过配置调整。超时处理确保 before Hook 不会因挂起而无限期阻塞工作流。

注意:before Hook 的退出码判断采用 Shell 标准约定——0 表示成功,非 0 表示失败。在 Shell 脚本中务必使用 exit 0 表示检查通过,exit 1(或其他非零值)表示检查不通过。

二、after Hook详解

after Hook 在事件执行之后触发,它的设计定位是"观察者模式"——可以获取事件的执行结果,但不能干预事件本身的执行流程。这与 before Hook 形成鲜明对比,两者互补构成了完整的 Hook 体系。

2.1 执行时序

after Hook 在事件执行完毕后立即触发,无论事件执行成功还是失败。系统会将事件的退出状态通过 $STATUS 环境变量传递给 after Hook 脚本,供后续处理逻辑使用。

执行流程: 用户操作 → 触发事件 → 执行事件 → 事件完成 → → 检查after Hook → 执行Hook脚本 → Hook执行完毕(无论退出码如何) → 返回结果给用户

2.2 核心特性

2.3 典型应用场景

日志记录
记录每次操作的详细信息、执行时间、结果状态,便于审计追踪。
通知发送
操作完成后发送 Slack/邮件/钉钉通知,告知相关人员操作结果。
清理回收
释放临时资源、删除临时文件、关闭数据库连接等清理工作。
指标采集
统计操作频率、成功率、耗时分布等运维指标数据。
提示:在 after Hook 中可以利用 $STATUS 实现条件分支逻辑。例如仅在事件执行失败时发送告警通知,或在成功时触发后续自动化流程。

三、before vs after对比

理解 before 和 after 两种 Hook 类型的差异,是正确设计 Hook 体系的基础。下面通过对比表格进行详细分析。

3.1 核心对比

对比维度 before Hook after Hook
执行时机 事件执行之前 事件执行之后
能否阻断事件 能(非零退出码可阻断) 不能(事件已被执行完毕)
适用场景 验证、检查、审批、环境准备 通知、日志、清理、度量
返回值要求 必须正确返回退出码(0通过/非0阻断) 退出码不影响主流程(仅用于日志记录)
异常处理方式 超时或失败时自动阻断事件 超时或失败时记录错误,不影响用户
能否修改输入 可以修改环境变量和输入参数 不能(事件已执行完毕)
典型用例 代码检查、权限验证、配置校验 日志记录、消息通知、资源清理
执行顺序 多个 before Hook 按序串联执行 多个 after Hook 按序串联执行

3.2 何时选择 before

3.3 何时选择 after

核心原则:如果你想让某件事"阻止"事件发生,使用 before Hook;如果你想让某件事"响应"事件发生,使用 after Hook。一个控制流程,一个观察流程。

四、混合使用策略

在实际项目中,before 和 after Hook 常常配合使用,形成完整的"检查-执行-记录"闭环。合理的混合使用策略能够最大化 Hooks 系统的价值。

4.1 同一事件的 before 和 after 搭配

最常见的模式是为同一个事件同时配置 before 和 after Hook,形成完整的生命周期管理:

配置示例(settings.json): { "hooks": { "beforeCommand": [ "check-env.sh", "validate-input.sh" ], "afterCommand": [ "log-command.sh", "send-notification.sh" ] } }

上述配置中,每次执行命令前会先运行 check-env.shvalidate-input.sh 进行前置检查,命令执行完成后会运行 log-command.shsend-notification.sh 进行后续处理。

4.2 多个 before Hook 的串联执行

当配置了多个 before Hook 时,系统会按照配置顺序依次执行。串联执行的链路中,任何一个 Hook 返回非零退出码都会立即阻断后续所有 Hook 和主事件的执行。

多个 before Hook 执行逻辑: beforeHook-1 执行 → 退出码为0 → 继续 → beforeHook-2 执行 → 退出码为0 → 继续 → beforeHook-3 执行 → 退出码非0 → 阻断! ✗ 不执行后续 beforeHook ✗ 不执行主事件 → 返回错误信息给用户

这种串联机制使得我们可以将不同职责的检查逻辑分离到独立的 Hook 脚本中,每个脚本只关注一件事情,保持关注点单一。例如将"权限检查"和"参数校验"分为两个脚本,各自独立维护。

4.3 多个 after Hook 的串联执行

多个 after Hook 同样按照配置顺序依次执行。与 before Hook 不同的是,after Hook 的退出码不会影响后续 after Hook 的执行——即使前面的 after Hook 失败了,后续的 after Hook 仍然会继续执行。

多个 after Hook 执行逻辑: 主事件执行完毕 → afterHook-1 执行 → 退出码非0 → afterHook-2 执行 → 退出码为0 → afterHook-3 执行 → 退出码非0 (所有 after Hook 都会执行完毕,相互不影响)

4.4 before 阻断后的 after 行为

一个重要的设计问题是:当 before Hook 阻断了事件执行,已配置的 after Hook 是否还会执行?答案是:不会。因为 before Hook 阻断意味着事件"从未被执行",自然也就不存在"事件执行之后"的状态。after Hook 仅在事件实际执行完成后才会触发。

重要:如果需要在 before Hook 阻断时仍然执行某些清理或日志逻辑,不应依赖 after Hook,而应在 before Hook 脚本自身中添加相应的错误处理分支。

五、Hook状态码和返回值

正确理解和使用 Hook 的退出状态码,是编写可靠 Hook 脚本的基础。Hook 系统遵循 Shell 标准的退出码约定,并在 before 和 after 两种场景中有不同的语义。

5.1 状态码约定

退出码 Shell 语义 before Hook 含义 after Hook 含义
0 成功 检查通过,允许事件继续执行 记录日志:Hook 执行成功
1 通用错误 检查失败,阻断事件执行 记录日志:Hook 执行失败
2 误用/参数错误 阻断事件,提示参数错误 记录错误日志,不影响主流程
126 命令不可执行 阻断事件,报告权限问题 记录错误日志
127 命令未找到 阻断事件,报告依赖缺失 记录错误日志
128+n 信号终止(信号 n) 阻断事件,报告被信号终止 记录错误日志
130 Ctrl+C 终止 阻断事件,报告用户中断 记录日志:Hook 被用户中断

5.2 Shell 脚本退出码示例

#!/bin/bash # before Hook 示例:检查磁盘空间 THRESHOLD=90 USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "错误:磁盘使用率已达 ${USAGE}%,超过阈值 ${THRESHOLD}%" exit 1 # 阻断事件执行 fi echo "磁盘空间检查通过,当前使用率 ${USAGE}%" exit 0 # 允许事件继续执行
#!/bin/bash # after Hook 示例:记录命令执行结果 STATUS=${STATUS:-$?} EVENT_NAME=${EVENT_NAME:-"unknown"} TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S") if [ "$STATUS" -eq 0 ]; then echo "[$TIMESTAMP] 事件 '$EVENT_NAME' 执行成功" # 可以在此处触发后续流程 else echo "[$TIMESTAMP] 事件 '$EVENT_NAME' 执行失败,状态码: $STATUS" # 可以在此处发送告警通知 fi exit 0 # after Hook 即使检测到失败也应返回 0,避免产生额外噪声

5.3 $STATUS 环境变量

$STATUS 是 after Hook 中特有的环境变量,用于传递主事件的退出状态码。在 Shell 脚本中可以通过以下方式获取:

最佳实践: