在 Claude Code 中,Hooks 的生命周期管理完全通过 settings.json 文件中的 hooks 节点进行集中配置。该节点位于全局配置或项目级配置中,定义了所有自动化行为的触发规则和执行逻辑。
settings.json 中的 hooks 节点结构如下:
before_hook 数组:注册在工具(Tool)调用之前触发的钩子。当一个事件被匹配时,系统会在执行目标工具之前,先依次执行 before_hook 数组中所有符合条件的 Hook。常用于权限校验、日志记录、环境准备等场景。
after_hook 数组:注册在工具调用之后触发的钩子。当工具执行完成(无论成功或失败),系统会执行 after_hook 中所有符合条件的 Hook。常用于结果通知、状态清理、数据持久化等场景。
每个 Hook 条目使用 match 字段定义触发条件,使用 command 字段定义要执行的命令或脚本路径。系统会遍历数组,从上到下依次检查每个 Hook 的 match 条件是否满足当前事件。
每个 Hook 条目是一个 JSON 对象,包含一组标准字段来精确控制其行为。以下是一段完整的示例配置:
各字段详细说明如下:
| 字段名 | 是否必填 | 说明 |
|---|---|---|
name | 推荐 | Hook 的唯一名称,用于日志和调试标识。建议使用短横线命名法。 |
description | 可选 | 对 Hook 功能的文字说明,帮助其他开发者理解该 Hook 的用途。 |
match | 是 | 匹配条件表达式,支持通过 $EVENT_TYPE、$TOOL_NAME 等变量进行条件判断,支持 ==、!=、&&、|| 和 glob 通配符。 |
command | 是 | 要执行的命令字符串或可执行脚本的路径。命令中可使用 $变量名 引用环境变量和 Hook 参数。 |
path | 可选 | 设置命令执行时的工作目录。未指定时使用项目根目录。 |
Match 表达式详解:match 字段使用类似编程语言的逻辑表达式语法,支持以下操作:
$TOOL_NAME == Read*(任意字符串)和 ?(任意单字符),如 $TOOL_NAME == Write*$TOOL_NAME == Read && $EVENT_TYPE == before$TOOL_NAME == Write || $TOOL_NAME == Edit($TOOL_NAME == Read || $TOOL_NAME == Write) && $EVENT_TYPE == after多个 Hook 的匹配优先级:Hook 的执行顺序严格遵循数组中的排列顺序。系统从上到下遍历数组,对每个 Hook 独立评估其 match 条件。所有 match 条件为 true 的 Hook 都会被执行。因此,如果希望某个 Hook 优先执行,应将其放在数组靠前的位置。不存在"短路"机制——所有匹配的 Hook 都会依次执行。
Hook 执行时会自动注入一组预定义的环境变量,开发者可以在 command 字符串或脚本中通过 $变量名 的方式引用它们。这些变量提供了当前事件的上下文信息。
| 变量名 | 说明 | 示例值 |
|---|---|---|
$EVENT_TYPE | 触发事件类型,值为 before 或 after,分别表示工具调用前和调用后。 | before |
$TOOL_NAME | 触发本次 Hook 执行的工具名称,如 Read、Write、Edit、Bash 等。 | Read |
$HOOK_NAME | 当前正在执行的 Hook 名称,即配置中的 name 字段值。 | log-tool-call |
$STATUS | 仅在 after_hook 中可用,表示前置工具的执行结果状态。0 表示成功,非零表示失败。 | 0 |
$OUTPUT_FILE | 在某些工具执行后,该变量指向工具输出内容所写入的临时文件路径,可用于后续处理。 | /tmp/claude-output-xxx |
command 中可以使用 echo "$HOOK_NAME triggered by $TOOL_NAME with status $STATUS" 等方式进行调试和日志记录,帮助排查 Hook 是否被正确触发。
跨 Hook 的环境变量共享:同一个 before_hook 数组中,前一个 Hook 可以通过 export 命令设置环境变量,后续的 Hook 可以读取这些变量。例如:
后续的 Hook 中可以通过 $HOOK_CONTEXT 引用该变量。但需注意,每个 Hook 可能在独立的 shell 进程中执行,跨进程变量共享需要通过文件系统或环境变量导出机制来实现。推荐的做法是将共享数据写入临时文件,后续 Hook 再读取。
在 Claude Code 的配置体系中,Hook 的启用和禁用主要通过以下几种方式实现:
最简单的禁用方式是将 JSON 中对应的 Hook 对象注释掉或移除。settings.json 本身是 JSON 格式,不支持原生注释。推荐的做法是将需要禁用的 Hook 条目从 before_hook 或 after_hook 数组中删除。如果希望保留配置以备将来启用,可以将其移到一个单独的备份数组中或暂时放在配置文件的注释区域(使用项目支持的注释语法)。
hooks 节点置空:"hooks": {}。切勿在 JSON 文件中直接写入 // 或 /* */ 注释,这会导致解析错误。
通过巧妙的 match 表达式设计,可以实现条件性的启用和禁用效果。例如,只在特定事件类型下触发:
利用 match 表达式的灵活性,可以实现精细化的条件控制:
$TOOL_NAME == NEVER_MATCH$EVENT_TYPE == before$EVENT_TYPE == after$TOOL_NAME != Bash$TOOL_NAME == Read || $TOOL_NAME == EditClaude Code 的 Hooks 系统在不同平台上的默认行为保持一致:
hooks 节点或节点为空,系统不会触发任何 Hook。Hook 默认是"禁用"状态,直到你显式配置。/reload-plugins 命令使配置生效。Hooks 的 command 字段最终会在操作系统的 Shell 中执行,因此跨平台兼容性是一个必须考虑的问题。Claude Code 支持 Windows、Linux 和 macOS 三大平台,不同平台默认使用的 Shell 不同。
| 平台 | 默认 Shell | 常见替代 |
|---|---|---|
| Windows | cmd.exe | PowerShell、Git Bash、WSL |
| Linux | /bin/bash | zsh、sh、dash |
| macOS | /bin/zsh(Catalina 起) | /bin/bash |
在 Windows 环境中,command 中的字符串会被传递给 cmd.exe 执行。这意味着:
echo、dir、type 等 cmd 原生命令\,但建议在 JSON 字符串中将其转义为 \\%VAR_NAME% 语法(cmd 原生),但 Hook 注入的 $EVENT_TYPE 等变量会被 Claude Code 在传递前替换,因此仍可使用 $EVENT_TYPE 语法powershell -Command "...".bat 或 .cmd)可以直接通过路径调用在 Linux 和 macOS 上,命令在 bash 或 zsh 中执行:
echo、ls、cat、grep 等 POSIX 命令/$VAR_NAME 语法|、重定向 >>、变量展开等高级特性bash script.sh 或直接执行脚本文件为了编写一次配置、多平台运行的 Hook,推荐以下策略:
推荐方案一:使用外部脚本文件
将复杂的 Hook 逻辑写入独立的脚本文件中(如 .sh、.bat、.ps1),在 command 中调用对应的脚本。不同平台使用不同的脚本文件,通过平台检测逻辑分支调用。
推荐方案二:使用平台无关的工具
优先使用 Node.js、Python 等跨平台运行时来执行 Hook 逻辑。在 command 中调用 node script.js 或 python script.py,这些脚本可以在所有平台上一致运行。
推荐方案三:条件分支命令
在单个 command 字符串中使用 shell 的条件判断来检测平台并执行不同逻辑:
使用 Node.js 作为跨平台执行引擎的优势在于:Claude Code 本身依赖 Node.js 运行环境,因此可以确保目标机器上已安装 Node.js,无需额外依赖。
1. 简单的日志记录和多平台兼容的 echo 可以直接在 command 中编写。
2. 复杂的业务逻辑建议使用 Node.js 脚本,天然跨平台且无额外依赖。
3. 平台特定的操作(如文件系统权限、进程管理)使用条件分支,在不同平台上执行不同的命令。
4. 始终注意路径分隔符的差异,推荐在脚本中使用 path 模块或 os.path 进行路径拼接。
5. 在 match 条件中精确限定触发范围,避免不必要的 Hook 执行影响性能。