内容来源:Totalum Blog。https://www.totalum.app/blog/claude-code-hooks-totalum
原题:Claude Code Hooks in 2026: A Production Playbook (PreToolUse, PostToolUse, Stop, SubagentStop)
原发布时间:2026-06-09

Claude Code Hooks 2026 年生产实践手册,涵盖 PreToolUse、PostToolUse、Stop、SubagentStop、安全护栏,以及 Skills 与 Hooks 的组合方案。

Totalum——面向人类与智能体的强大 AI 应用构建平台

Hooks 也能控制 Anthropic 托管沙箱内的工具执行;有关定时任务、沙箱与 MCP 隧道的完整配置,请参阅我们的 Anthropic Managed Agents 生产实践手册

Claude Code Hooks 是事件驱动的 Shell 命令或脚本,会在 Claude Code 工具调用生命周期的固定节点确定性运行。借助 Hooks,你不必相信模型会记住指令,也能阻止破坏性命令、强制执行代码风格规则、清除敏感信息并运行测试。到 2026 年,Hooks 已扩展到十余种事件,包括 PreToolUse、PostToolUse、Stop、SubagentStop、UserPromptSubmit、Notification、PreCompact 和 SessionStart,同时增加了 hookSpecificOutput.additionalContextbackground_taskssession_crons 等新字段。本指南是 Totalum 在真实工程环境中使用的生产实践手册,旨在帮助你交付经得住实际开发工作的 Hooks;其中还包含一套 Skills + Hooks 组合方案,以及可以直接复用的 Totalum MCP 示例。

若想了解如何并行启动子智能体,并通过 SubagentStop Hook 对其返回结果设置关卡,请参阅我们的 Claude Code 子智能体生产实践手册

如果你希望在读完本文的当天,就把 Claude Code 智能体接入真实生产技术栈(身份验证、数据库、支付和部署),可以在 totalum.app 连接 Totalum,并从第一次会话起使用下面介绍的 Hook 模式。

快速结论

• Claude Code Hooks 是通过 JSON 配置的 Shell 命令,会在生命周期事件发生时触发,包括 PreToolUse、PostToolUse、Stop、SubagentStop、UserPromptSubmit、Notification、PreCompact、SessionStart、SessionEnd 等。
• PreToolUse 可以在工具运行前以退出码 2 阻止操作。PostToolUse 可以在工具成功后执行格式化、Lint 或测试,但无法撤销已经发生的调用。
• Stop 与 SubagentStop 现在可以返回 hookSpecificOutput.additionalContext,以新上下文延续当前轮次;其输入中还会收到 background_taskssession_crons
• 用 Hooks 实施确定性护栏(安全、风格、测试);用 Skills 保存模型在相关情境下加载的流程知识;用子智能体开展拥有独立上下文窗口的并行子对话。
• 把 Skill 与 Hook 结合起来,既让模型知道应该做什么,又强制它落实。下文会展示这种模式。

如今,Hooks 和 Auto Mode 已成为更庞大的 Claude Code 平台体系的一部分;这套体系于 6 月 10 日举行的 Code with Claude Tokyo 活动上集中亮相。有关首日发布的 11 项新能力,请参阅 Code with Claude Tokyo 2026 回顾

Claude Code Hooks 到底是什么

Hook 是 Claude Code 在特定生命周期事件触发时同步运行的一条 Shell 命令。运行时通过标准输入传入 JSON,读取标准输出与标准错误,并根据退出码采取行动。是否调用 Hook 从来不由模型决定,模型也无法绕过它——这正是 Hook 存在的意义。凡是需要模型“始终记住”的事情,都应该考虑交给 Hook 强制执行。

Hooks 写在 .claude/settings.json(团队共享,应提交到仓库)或 .claude/settings.local.json(个人配置,应加入 gitignore)中。最小配置如下:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "./.claude/hooks/block_rm_rf.sh" }
        ]
      }
    ]
  }
}

matcher 是用来匹配工具名称的正则表达式。command 可以是 Shell 能运行的任何东西:Bash 脚本、Python 文件、Node 脚本、二进制程序,甚至是向内部服务发起的 curl 调用。务必让它足够快,因为现在每次 Bash 工具调用都要等这个 Hook 返回后才能继续。

一张表看懂 2026 年的生命周期

事件 触发时机 能否阻止? 典型用途
SessionStart 新的 Claude Code 会话开始时 注入项目上下文、设置环境、预热缓存
UserPromptSubmit 用户发送提示词时 是(退出码 2 = 拒绝) 拒绝提及生产密钥的提示词、记录审计轨迹
PreToolUse 工具调用即将执行前 是(退出码 2 = 阻止) 阻止 rm -rf、强制代码审查、要求填写工单 ID
PostToolUse 工具调用成功返回后 否(工具已经运行) 自动格式化、运行测试、推送到 CI、清除 diff 中的敏感信息
Notification Claude 发出通知时 同步到 Slack、写入可观测性系统
PreCompact 上下文压缩即将发生前 保存状态快照、记录产物位置
Stop 顶层智能体轮次结束时 特殊:additionalContext 可令其继续 添加后续检查清单、获取缺失数据
SubagentStop 派生的子智能体结束时 特殊:additionalContext 可令其继续 向父智能体汇总结果、控制合并
SessionEnd 会话终止时 刷新日志、持久化记忆、发布总结

其中有两个特殊事件。Stop 与 SubagentStop 已获得升级:Hook 可以返回 { "hookSpecificOutput": { "additionalContext": "..." } },把新内容送回当前轮次,而不会被视为错误。它们的输入载荷现在也包含 background_taskssession_crons,因此 Hook 可以在判定当前轮次是否真的应该结束之前,查看仍有哪些任务处于计划之中。

退出码与通信契约

Hooks 通过三个通道通信:退出码、标准输出和标准错误。这套契约很简单,值得记住。

• 退出码 0:成功。如果相应事件允许,标准输出会被追加到模型上下文中;标准错误只向用户显示。
• 退出码 2:硬性阻止。操作被取消,标准错误会作为原因反馈给 Claude。这是实现安全护栏的关键退出码。
• 其他非零退出码:错误。工具调用失败,模型能看到失败信息,但该操作并不会被明确标记为“已阻止”。

如果要注入结构化数据,请向标准输出写入 JSON。Hooks 现在接受顶层 hookSpecificOutput 对象,Stop / SubagentStop 会从中读取 additionalContext。PreToolUse 则会读取同一对象中的 permissionDecisionpermissionDecisionReasonmodifyInput,因此你也可以动态重写工具输入,而不必直接阻止调用。

模式一:用 PreToolUse 护栏阻止破坏性命令

这是每个 Claude Code 团队今天就应该添加的第一个 Hook。它可以防止模型删除你的主目录、清空生产数据库或强制推送 main 分支。脚本如下:

#!/usr/bin/env bash
# .claude/hooks/block_destructive.sh
set -euo pipefail
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command // ""')

deny='(^|[^A-Za-z])(rm[[:space:]]+-rf?|drop[[:space:]]+database|truncate[[:space:]]+table|git[[:space:]]+push[[:space:]]+.*--force|psql.*--command=.*delete[[:space:]]+from)'

if echo "$cmd" | grep -qiE "$deny"; then
  echo "Blocked by guard rail: command matches destructive pattern. Reword the request, or ask a human to run this manually." >&2
  exit 2
fi
exit 0

把它接入带有 Bash 匹配器的 PreToolUse 后,智能体每次尝试调用 Shell 时都会执行这段脚本。模型看到的拒绝原因就是标准错误里的文本,因此要让它具有可操作性:告诉模型下一步该怎么做,而不只是哪里出了问题。我们曾经吃过这个亏——由于拒绝消息没有提供替代方案,Claude 在循环中连续九次尝试了同一条被阻止的命令。

模式二:用 PostToolUse 格式化并测试

PostToolUse 会在工具成功后运行。它可以充当自动格式化工具、Linter 和快速测试运行器。由于工具已经执行完毕,这个 Hook 无法阻止操作,但它可以:

• 对已更改文件运行 ruff formatprettier --writegofmt
• 只运行被修改模块的单元测试。
• 通过标准输出把发现的问题追加到模型下一轮的上下文中。

下面是一个用于格式化并回报结果的小型 Python 包装脚本:

#!/usr/bin/env python3
# .claude/hooks/post_edit.py
import json, subprocess, sys

data = json.load(sys.stdin)
paths = data.get("tool_response", {}).get("written_files", [])

py = [p for p in paths if p.endswith(".py")]
if py:
    subprocess.run(["ruff", "format", *py], check=False)
    r = subprocess.run(["ruff", "check", *py], capture_output=True, text=True)
    if r.returncode != 0:
        print(json.dumps({
            "hookSpecificOutput": {
                "additionalContext": f"Lint findings:\n{r.stdout}\nFix before continuing."
            }
        }))
sys.exit(0)

这套模式的核心是:静默完成格式化,只呈现模型真正需要处理的信息。不要用成功的 Lint 输出塞满上下文,否则模型会开始总结这些噪声,而不是解决任务。

模式三:用 UserPromptSubmit 清理密钥

这个 Hook 能从源头防止密钥进入 Claude 对话。它检查用户提示词,并在检测到明显凭据时拒绝提示词(或重写它)。

#!/usr/bin/env bash
# .claude/hooks/scrub_secrets.sh
input=$(cat)
prompt=$(echo "$input" | jq -r '.user_prompt // ""')

if echo "$prompt" | grep -qE '(AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9]{32,}|-----BEGIN [A-Z ]+PRIVATE KEY-----)'; then
  echo "Prompt contains what looks like a credential. Redact it and resend." >&2
  exit 2
fi
exit 0

这个 Hook 的进阶版本会输出 { "hookSpecificOutput": { "modifyInput": { "user_prompt": "..." } } } 来重写提示词,在模型看到密钥前就用占位符替换它。这是最安全的用户体验,因为智能体仍然能够回应用户的真实意图。

模式四:用 Stop Hook 延续当前轮次

Stop Hook 就是你的“真的做完了吗?”检查器。2026 年的契约允许它返回 hookSpecificOutput.additionalContext,无需触发 Hook 错误就能加入一条新指令。你可以用这种方式实现柔性的完成关卡。

#!/usr/bin/env python3
# .claude/hooks/stop_gate.py
import json, sys
data = json.load(sys.stdin)
bg = data.get("background_tasks", [])
crons = data.get("session_crons", [])

if bg or crons:
    print(json.dumps({
        "hookSpecificOutput": {
            "additionalContext": (
                f"{len(bg)} background task(s) and {len(crons)} cron(s) are still scheduled. "
                "Confirm they are intentional, or cancel them before ending the turn."
            )
        }
    }))
sys.exit(0)

现在,模型会看到这段附加上下文,然后说明为什么要保留后台工作,或者将其清理掉。从用户视角看,Claude 会把事情完整收尾,而不是遗留一个只启动了一半的任务。

模式五:用 SubagentStop Hook 控制合并

当你通过 Task 工具或 Agent SDK 把工作委派给子智能体时,子智能体的对话结束便会触发 SubagentStop。你可以用它强制执行一份契约:子智能体必须写好报告文件、发起 PR 或生成结构化产物,其结果才能被视为最终成果。如果没有履行契约,就返回 additionalContext,要求父智能体返工或拒绝该结果。

这里很适合强制执行诸如“每个研究子智能体必须引用至少两个来源”或“每个重构子智能体都必须附带测试 diff”之类的规则。Skills 定义流程,Stop Hook 强制验收结果。你也可以将其与一种 Claude Code 子智能体 模式对比;后者通过 Agent SDK 控制并发。

Skills + Hooks:组合方案

我们在 Claude Code Skills 指南中介绍过 Skills 这一动态流程知识层。Skill 教模型如何做某件事;Hook 则确保模型确实以正确方式完成它。

组合步骤如下:

1、编写一个名为 database-migration 的 Skill,记录团队的迁移流程,包括格式、可逆性与命名规则。
2、编写一个 PostToolUse Hook,在每次写入 .sql 文件时运行:执行团队的迁移 Linter、检查不可逆操作,并在缺少任何必要内容时通过 additionalContext 提出异议。
3、添加一个 Stop Hook,要求模型至少运行一次 migrate dry-run,然后才能宣称迁移已经就绪。

Skill 让模型胜任工作,Hooks 让模型对结果负责。二者缺一不可:只有 Skill,它就只是一条礼貌建议;只有 Hook,模型则只能猜测规则到底想要什么。我们在 Totalum 代码库内部使用的正是这套模式;上线后,与迁移相关的值班事故降到了零。

如果你还不确定该在什么时候使用哪一种机制,可以参考我们在 Skills 指南中发布的同一张表:

需求 Skills Hooks 子智能体
模型在相关情境下加载的流程知识
无法跳过的硬性约束
使用隔离上下文的并行子对话
每次都确定性运行
即使模型行为异常也仍然有效 部分有效
可跨团队、跨仓库组合

模式六:为 Totalum MCP 服务器添加 Hook

Totalum 提供了 MCP 服务器,因此 Claude Code 智能体能够读写项目中的数据表、文件、部署与密钥。当 Claude Code 智能体通过这个 MCP 修改生产数据时,你应该用 Hook 强制执行两条规则:智能体会话只能修改指定的数据表;每次修改都必须连同运行该会话的用户一起写入日志。

下面是一个使用 mcp__totalum__.* 匹配器的 PreToolUse Hook:

#!/usr/bin/env python3
# .claude/hooks/totalum_guard.py
import json, sys, os, urllib.request, time

data = json.load(sys.stdin)
tool = data.get("tool_name", "")
ti = data.get("tool_input", {})

if tool.endswith("createRecord") or tool.endswith("editRecordProperties") or tool.endswith("deleteRecordById"):
    table = ti.get("typeId", "")
    if table in {"users", "billing", "production_secrets"}:
        print("Totalum guard: this table is read-only from agent sessions. Ask a human.", file=sys.stderr)
        sys.exit(2)

    audit = {
        "ts": int(time.time()),
        "tool": tool,
        "table": table,
        "session_user": os.environ.get("CLAUDE_USER", "unknown"),
    }
    req = urllib.request.Request(
        os.environ["AUDIT_WEBHOOK_URL"],
        data=json.dumps(audit).encode(),
        headers={"Content-Type": "application/json"},
    )
    urllib.request.urlopen(req, timeout=2)
sys.exit(0)

这样一来,智能体仍能高效处理真实数据,但约束它们的是 Shell 代码构成的护栏,而不是模型的自觉。更多 MCP 接线模式可参阅我们的 2026 年最佳 MCP 服务器清单和 Claude Code MCP 教程

性能与超时

每个 PreToolUse Hook 都会给所有匹配的工具调用增加延迟。关键路径上的执行时间应控制在 100 毫秒以内。以下方法行之有效:

• 缩小匹配范围。在 tool_name 上使用具体的正则表达式,不要用 .*
• 低成本跳过无关工作。先读取输入,如果无需处理就立即返回成功。
• 把重型检查放到 PostToolUse。工具已经执行完毕;此时模型等待的是摘要,而不是执行许可。
• 将慢速工作放到后台。利用新的 background_tasks 槽位处理上传审计日志等任务。

如果 Hook 需要调用外部服务,请设置较短的超时时间(1~2 秒);超时后应开放通过,同时记录警告。如果一律采取失败即关闭的策略,那么每次 Claude Code 会话都会沦为审计服务可用性的“人质”。

安全检查清单

Hooks 是第一道防线,但并非万能。向团队发布 Hooks 配置前,请逐项检查以下内容。

• 把 .claude/settings.json 当作代码管理:在 PR 中审查变更、要求批准并对提交签名。
• 将 Hook 脚本固定放在仓库的 .claude/hooks/ 中,像审计其他构建脚本一样审计它们。在开发者机器上,恶意 PR 添加一个 Hook,可能就是成本最低的供应链攻击。
.claude/settings.local.json(应被 gitignore)只用于个人覆盖配置。凡是用于保护生产环境的规则,都应该写入共享文件。
• 对从标准输入读取的 JSON 做净化处理。应把 commanduser_prompttool_input 等字段视为不可信输入。
• 把 Hook 的决策(包括拒绝)记录到审计流水线中。当工程师追问某条命令为何被阻止时,你会需要这些记录。
• 测试你的 Hooks。我们在 tests/hooks/ 目录中保存黄金输入和预期退出码,并在每次 CI 构建时运行这些测试。

哪些时候不该使用 Hooks

Hooks 解决不了不清晰的产品规格,也无法修复混乱的团队协作。如果模型做错事是因为它不知道正确做法,那就先编写 Skill 或 CLAUDE.md,再考虑 Hook。如果模型知道正确做法,却执行得不稳定,Hook 才是正确选择。可以这样理解:Hooks 对抗的是“不遵守”,而不是“不知道”。

Hooks 也不适合承载业务逻辑。如果某条规则既要约束智能体,也要约束人类,就应该把它放进 CI 检查、数据库触发器或部署关卡。Hooks 按定义只作用于智能体;一条只存在于 .claude/settings.json 中的重复规则,会变成隐蔽的生产假设,让下一位工程师措手不及。

可直接复用的仓库骨架

以下是我们在每个 Totalum 仓库中使用的结构:

.claude/
  settings.json          # team-shared hooks config, in git
  settings.local.json    # personal, gitignored
  hooks/
    block_destructive.sh
    post_edit.py
    scrub_secrets.sh
    stop_gate.py
    subagent_gate.py
    totalum_guard.py
  skills/
    database-migration.md
    deploy-runbook.md
tests/
  hooks/
    test_block_destructive.sh
    test_post_edit.py

提交这些脚本,也提交相应测试。把 .claude/ 当成工程流程中的一等公民,而不是个人配置角落。我们在 Claude Code 定价分析中也建议采取同样的态度:无人监督的智能体所带来的成本,源头并不是按 Token 计费,而是缺少护栏。

如果你想并排比较其他智能体运行时如何采用不同方式处理这类问题,可阅读 Cline 与 Claude Code 对比,了解两款产品各自如何拦截生命周期事件。

Hooks 与数据库工具配合得很好;若想了解其中 PostgreSQL 这一半,请参阅更深入的 Claude Code PostgreSQL MCP 解读

常见问题

PreToolUse 与 PostToolUse 有什么区别?

PreToolUse 在工具调用运行前触发,可以通过退出码 2 阻止调用。PostToolUse 在工具调用成功后触发,可以对结果执行格式化、Lint 或测试,但无法撤销这次工具调用。用 PreToolUse 建立护栏;用 PostToolUse 做清理、验证和反馈。

Hooks 能否重写工具输入,而不是直接阻止调用?

可以。PreToolUse Hook 可以向标准输出写入 { "hookSpecificOutput": { "modifyInput": { ... } } }。运行时会把修改后的输入应用于工具调用。借助这种方式,你可以清除密钥、规范化路径或限制参数范围,而不必拒绝整个调用。

2026 年的 Stop 与 SubagentStop Hooks 有什么作用?

二者都会在智能体(顶层智能体或子智能体)结束一轮工作时触发。其输入中包含 background_taskssession_crons,因此它们能够查看仍在计划中的任务。它们可以返回 hookSpecificOutput.additionalContext,加入新的上下文并让当前轮次继续,而不是立即结束。Stop 适合用作完成关卡,SubagentStop 适合强制执行合并契约。

Hooks 与 Skills、子智能体相比有什么不同?

Skills 教模型如何做某件事,Hooks 强制它把事情做对,子智能体则在各自独立的上下文中运行子对话。三者相辅相成:真正的生产配置会同时使用三者。并排对比可参阅我们的 Claude Code Skills 指南

Hooks 配置应该放在哪里?

团队共享规则应写入 .claude/settings.json 并提交到仓库。个人覆盖配置应写入 .claude/settings.local.json,而且该文件应该被 gitignore。凡是用于保护生产环境的规则都应该放在共享文件中,避免团队成员悄悄将其禁用。

运行缓慢的 Hook 会拖慢每次 Claude Code 调用吗?

如果它匹配每次工具调用,答案是肯定的。PreToolUse Hooks 应控制在 100 毫秒以内,用具体的正则表达式缩小匹配范围,并把耗时检查放到工具已经执行完毕的 PostToolUse 中。调用外部服务时,请设置 1~2 秒的超时;如果审计服务出错,应开放通过并发出警告,绝不能一律失败关闭。

Hook 可以调用 Totalum MCP 服务器吗?

可以。Hook 脚本就是普通的 Shell 或 Python 代码,因此可以调用任何 HTTP API,包括 Totalum 的 API。本指南中的模式利用 PreToolUse Hook 强制敏感数据表只读,并在每次修改时写入审计记录。相关接线方式可参阅 Claude Code MCP 教程2026 年最佳 MCP 服务器

准备好把 Claude Code 智能体接入真实后端了吗?

Hooks 是高效智能体循环的后半部分。前半部分,是为智能体准备一个真正可供构建的生产后端:身份验证、数据库、支付、文件存储、部署和自定义域名。Totalum 提供了这一切,还提供了智能体可以调用的 MCP 服务器;从第一次会话开始,你就能在它上面使用本指南中的 Hook 模式。

现在前往 totalum.app 免费开始使用,并连接你的 Claude Code 智能体。

如果你的 Hook 策略会按模型层级分派子智能体,还要把 2026 年 6 月 9 日发布的 Claude Fable 5 纳入考虑。Fable 5 是新的高级层级,每百万 Token 的输入/输出价格为 10/50 美元,上下文窗口为 100 万 Token;它在路由表中的位置高于 Opus 4.8 与 Haiku 4.5。

当前版本准确性说明(核对于 2026-07-21)

• 本文是 Totalum 带有产品推广性质的文章,并非 Anthropic 官方博客。
• 生命周期表并不完整。当前 Claude Code 的 Hook 事件要多得多,还包括权限、任务、配置、工作树、文件变更、压缩和 MCP elicitation 等事件。当前 PreCompact 可以通过退出码 2 阻止压缩。
• Hooks 不只支持同步 Shell 命令。根据事件类型不同,处理程序可以是命令、HTTP、MCP 工具、提示词或智能体;相匹配的处理程序会并行运行。
• 原文反复输出 hookSpecificOutput,但没有提供必需的 hookEventName。结构化示例应该把该字段设置为当前事件,例如 PostToolUse 或 Stop。
• PreToolUse 重写输入使用的是 hookSpecificOutput.updatedInput,而不是 modifyInput。替换值必须包含期望的完整工具输入,未修改的字段也不能省略。
• UserPromptSubmit 输入使用的字段是 prompt,而不是 user_prompt,因此原文的密钥清理脚本实际检查的是空字符串。UserPromptSubmit 可以阻止请求或添加上下文,但无法重写原始提示词。
• PostToolUse 格式化脚本假定存在 tool_response.written_files。当前工具响应因工具而异;例如 Write 使用 filePathsuccess 等字段。Hook 必须匹配 Edit/Write,并解析文档中规定的各种响应结构。
• PostToolUse 无法撤销已经发生的操作。它可以提供反馈,或在下一次模型调用前停止智能体循环,但 additionalContext 本身并不代表拒绝。
• SubagentStop 的 additionalContext 会让子智能体继续运行,并不会向父智能体注入指令。如果要在子智能体返回后向父会话添加上下文,当前文档建议使用匹配 Agent 工具的 PostToolUse Hook。
background_taskssession_crons 是 Stop/SubagentStop 输入中的只读状态数组,并不是用来启动耗时后台审计上传任务的槽位。PostToolUse 同样位于下一次模型请求之前的关键路径上。
• 原文仓库骨架在 .claude/skills/ 下使用扁平文件。当前 Skills 应采用 .claude/skills/<skill-name>/SKILL.md 结构;扁平提示词文件应放入仍然兼容的旧式 .claude/commands/ 目录。
• 项目 Hooks 可以共享,但并不是不可改变的生产安全边界:它们可能失败、超时、被禁用,或遭到托管策略阻止。还应结合使用托管设置、最小权限、沙箱、服务端授权、CI 以及数据库/部署控制。
• 破坏性命令正则表达式只是示例,并不能提供完整保护。替代参数、Shell 语法、脚本、解释器、别名、编码后的命令以及非 Bash 工具都可能绕过它。
• 原文的两个 Bash 示例都包含字面文本 &gt;&2。这是被双重转义的 HTML 残留,并不是有效的标准错误重定向;实际使用前应改为 >&2
• 采用失败开放还是失败关闭,取决于具体风险,不能一概而论。审计遥测可以失败开放,而授权与破坏性操作控制通常需要采用失败关闭设计。
• Claude Fable 5 的发布日期、每百万 Token 10/50 美元的价格与 100 万 Token 上下文声明,与 Anthropic 当前模型文档一致;模型可用性与价格仍具有时效性。