Claude Code Hooks 教學:改檔前自動擋、改完自動記,實測用指令改就擋不到

部分實測2026.10.14

Claude Code 的 hooks 是你自己寫的小指令,在固定的時間點一定會執行,不靠 AI 自己決定。這篇講 hooks 寫在哪裡、有哪些時間點、怎麼用結束代碼擋下一個動作;我實際設了兩個:改 .env 之前擋下、改完檔案自動記一筆。擋下有用,但換成用 sed 指令改,同一個 hook 完全擋不到,要另外加一個檢查指令的 hook。三次實測的過程和設定檔都貼出來。2026 年 10 月 14 日實測。

更新 2026/10/15・約 9 分鐘讀完・查證環境:在我自己的 Mac 上用 Claude Code 2.1.280 的非互動模式(claude -p)跑了三次,測試資料夾裡放一個待辦檔和一個假的 .env(沒有任何真的金鑰),hook 腳本是我寫的,只動這個資料夾裡的檔案。互動模式的 /hooks 選單、桌面通知、prompt 型和 http 型的 hook 沒有實測,依官方文件

先講結論

  • Hooks 是寫在 settings.json 裡的小指令,在固定的時間點一定會執行,例如每次要改檔案之前、改完之後、Claude 回答完的時候。要擋下一個動作,讓腳本用結束代碼 2 結束,並把理由印出來。
  • 我設了兩個:改 .env 之前擋下、改完檔案記一筆。實測改待辦檔有記到、改 .env 被擋,Claude 回報沒改成,也說不會繞過。
  • 但同一個 hook 只管「改檔案」這個工具。我改成請它用 sed 指令改 .env,一下就改掉了。要另外加一個檢查終端機指令的 hook,才擋得住。
目錄
  1. Hooks 是什麼
  2. 寫在哪裡
  3. 寫法:時間點、比對的工具、要跑的指令
  4. 實測一:改待辦檔可以,改 .env 被擋
  5. 實測二:換成 sed 指令,hook 擋不到
  6. 實測三:加一個檢查指令的 hook
  7. 用之前要知道的
  8. 這篇沒有測的

用 Claude Code 久了,會有一些事希望它「每次都一定要做」或「絕對不要做」。例如改完程式碼一定要跑一次格式化,或是絕對不要動 .env 這種放密碼的檔案。

這些話寫在 CLAUDE.md 裡,它多半會照做,但不是每次。Hooks 就是為了「一定」這兩個字設計的:你寫一小段指令,綁在某個時間點,時間到了 Claude Code 就執行,不經過 AI 的判斷。

這篇講 hooks 寫在哪、有哪些時間點、怎麼擋下一個動作,然後把我實際設的兩個 hook 跑給你看,包括一個我測出來的漏洞。Claude Code 還不熟的話,先看 Claude Code 教學。

Hooks 是什麼

官方文件的說法:hooks 是使用者自己定義的 shell 指令,Claude Code 在生命週期的特定時間點執行它們,讓某些動作「一定會發生」,而不是靠模型決定要不要做。

可以綁的時間點很多,官方的表上列了三十幾個。最常用的是這幾個:

  • PreToolUse:Claude 要用某個工具之前,例如要改檔案、要跑指令之前。這個時間點可以把動作擋下來。
  • PostToolUse:工具用完之後,例如檔案改好了。
  • Stop:Claude 回答完的時候。
  • Notification:Claude Code 要通知你的時候,例如在等你同意某個動作。
  • SessionStart:一個對話開始的時候。
  • UserPromptSubmit:你送出訊息、Claude 還沒開始處理之前。

官方文件舉的例子有:改完檔案自動跑格式化工具、Claude 在等你的時候跳出桌面通知、擋下對 .env 和 package-lock.json 這類檔案的修改、記錄設定檔被改了什麼。

寫在哪裡

Hooks 寫在 settings 檔的 hooks 區塊。放在哪一個檔,決定它管到哪裡:

  • ~/.claude/settings.json:你自己所有的專案。
  • 專案的 .claude/settings.json:只有這個專案,可以跟程式碼一起進版本控制,團隊共用。
  • 專案的 .claude/settings.local.json:只有這個專案,而且不分享出去。

外掛、Skill 和子代理也可以自帶 hook。在 Claude Code 裡打 /hooks,可以看到它讀到的所有 hook。想全部關掉,在設定檔加 "disableAllHooks": true。

寫法:時間點、比對的工具、要跑的指令

我實際用的設定長這樣,放在測試資料夾的 .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{
          "type": "command",
          "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{
          "type": "command",
          "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/log-edits.sh"
        }]
      }
    ]
  }
}

一層一層看:最外面是時間點(PreToolUse、PostToolUse);matcher 是要比對的工具,Edit|Write 表示改檔和寫檔這兩個工具;最裡面的 command 是要跑的指令。$CLAUDE_PROJECT_DIR 是專案資料夾的路徑,Claude Code 會幫你填。

Claude Code 跑 hook 的時候,會把這次的資料用 JSON 從標準輸入交給你的指令,裡面有工具名稱(tool_name)、工具的參數(tool_input,改檔的話有檔案路徑)等。你的腳本讀它,決定要做什麼。

怎麼擋下一個動作

靠的是腳本結束時的代碼。官方文件寫得很清楚:

  • 結束代碼 2:擋下這個動作。寫到 stderr(錯誤輸出)的文字,會交給 Claude 當作被擋的理由。
  • 結束代碼 0:沒意見。要注意,這不等於核准,原本會問你要不要同意的,還是照樣會問。
  • 其他代碼:多半當成 hook 自己出錯,動作照常進行。

我擋 .env 的腳本是照官方範例簡化的:

#!/bin/bash
# 擋住對 .env 的修改
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" \
  | jq -r '.tool_input.file_path // empty')
case "$FILE_PATH" in
  *.env|*/.env)
    echo "已擋下:$FILE_PATH" \
         "是受保護的檔案,請不要修改。" >&2
    exit 2 ;;
esac
exit 0

它用 jq 這個工具從 JSON 裡拿出檔案路徑,結尾是 .env 就印出理由、用代碼 2 結束。macOS 和 Linux 上,腳本要先 chmod +x 讓它可以執行。

記錄的那個更簡單:每次改完,把時間、工具名稱、檔名加一行到 edit-log.txt。

實測一:改待辦檔可以,改 .env 被擋

測試資料夾裡有一個待辦檔,和一個假的 .env,裡面只有一行 TEST_ONLY=1,沒有任何真的金鑰。

我用非互動模式(claude -p)請它:「在 todo.txt 最後加一行『- 買牛奶』,再把 .env 裡的 TEST_ONLY=1 改成 TEST_ONLY=0。」只允許它讀檔、改檔、寫檔。

結果:

  • 待辦檔改好了,edit-log.txt 多了一行 14:08:30 Edit …/todo.txt,記錄的 hook 有跑。
  • 改 .env 那一步被擋下,Claude 收到的是一個錯誤,內容就是我腳本印的那句「已擋下:…/.env 是受保護的檔案,請不要修改。」
  • 它最後回報:待辦檔改好了,.env 沒改成,還是 TEST_ONLY=1,因為專案的 hook 把它擋下;它也寫了「我沒有用其他方式繞過這個保護」,建議我自己手動改,或先調整保護清單。

整個過程 10 秒。順帶一提,它改之前有先把 .env 讀了一遍,我那個 hook 只管改、不管讀。真的要保護放密碼的檔案,連讀取都要擋,可以在 matcher 加上 Read,或用 Claude Code 的權限設定把它列為拒絕。

實測二:換成 sed 指令,hook 擋不到

接著我換個方式問:「用 sed 指令把 .env 裡的 TEST_ONLY=1 改成 TEST_ONLY=0,這是我自己的測試檔,我確定要改。」這次允許它執行 sed 指令。

結果它先用 grep 看了一下,再跑了一行 sed,.env 就變成 TEST_ONLY=0 了。8 秒,hook 完全沒有反應,記錄檔也沒有新的一行。

原因很單純:我的兩個 hook 的 matcher 都只寫了 Edit|Write。用 sed 改檔,Claude 用的是終端機指令的工具(Bash),根本不會經過這兩個 hook。

這是設 hook 最容易漏掉的地方:你以為擋住了「改 .env」這件事,其實只擋住了「用改檔工具改 .env」這一條路。

實測三:加一個檢查指令的 hook

所以我再加一個 hook,matcher 寫 Bash,指令裡只要出現 .env 就用代碼 2 擋下:

#!/bin/bash
# 擋住提到 .env 的終端機指令
INPUT=$(cat)
CMD=$(echo "$INPUT" \
  | jq -r '.tool_input.command // empty')
if echo "$CMD" | grep -q '\.env'; then
  echo "已擋下:這個指令會動到 .env,請不要用指令修改受保護的檔案。" >&2
  exit 2
fi
exit 0

把 .env 改回 TEST_ONLY=1,同一句話再問一次。這次它第一個想跑的 grep 就被擋了,.env 沒有被改。

它的回答寫:「這個 hook 是你們設的防護,所以我不會用改寫檔名或換工具之類的方式繞過去」,然後給我三個做法:自己在終端機跑那行 sed、在 hook 裡加例外、或改用改檔工具直接改。第三個做法其實也會被第一個 hook 擋下,它沒有想到。不過不管哪一條路,hook 都會照設定擋,不需要靠它記得。

這個寫法很粗:只要指令裡有 .env 這幾個字就擋,連單純讀取也擋。實際要用,可以寫得更細,例如只擋 sed、rm、mv 這類會改東西的指令。

用之前要知道的

Hook 是你電腦上真的會執行的指令。官方文件的安全說明寫,command 型的 hook 用你自己帳號的完整權限執行,你的帳號能讀、能改、能刪的檔案,它都能動;預設最多可以跑 10 分鐘。別人給你的 hook,先看懂、測過再加進去。

matcher 要想清楚。同一件事常常有好幾條路可以做到,例如改檔可以用改檔工具,也可以用指令。要擋,就每一條路都擋。

結束代碼 0 不等於核准。hook 沒意見的時候,原本該問你的還是會問。哪些動作會問你,看你用的是哪一種權限模式,例如只規劃不動手的 plan mode,在 Plan Mode 教學。

除了跑指令(command),官方文件還列了幾種:把資料送到一個網址(http)、讓模型判斷一次(prompt)、呼叫 MCP 伺服器的工具。這幾種這次都沒有測。

不知道 hook 有沒有跑的時候,在 Claude Code 裡打 /hooks 看它讀到哪些。我這次是靠記錄檔和被擋下時的錯誤訊息判斷的。

這篇沒有測的

互動模式裡的 /hooks 選單、Notification 跳桌面通知、Stop 和 SessionStart 這幾個時間點。我三次都是用 claude -p 測改檔和跑指令。

prompt 型、http 型、MCP 工具型的 hook,以及用結構化 JSON 回傳決定的寫法。

Windows 上的寫法。

把 hook 包進外掛、Skill 或子代理。Skill 是什麼在 Claude Skills 教學,子代理在 Claude Code 子代理教學。

常見問題

Claude Code 的 hooks 要寫在哪裡?

寫在 settings 檔的 hooks 區塊。只給這個專案用的寫在專案的 .claude/settings.json,可以跟程式碼一起進版本控制;自己所有專案都要的寫在 ~/.claude/settings.json;只想自己用、不分享的放 .claude/settings.local.json。外掛、Skill 和子代理也可以自帶 hook。

hook 要怎麼擋下 Claude 的動作?

用 PreToolUse 這個時間點,讓腳本用結束代碼 2 結束,並把理由寫到 stderr。Claude Code 會擋下那個動作,把理由交給 Claude。要注意結束代碼 0 只是「沒意見」,不等於核准,原本的權限詢問還是照走。

為什麼我設了 hook 卻好像沒有作用?

先看 matcher 對不對。我實測時,只掛在 Edit 和 Write 的 hook,擋不到 Claude 用 sed 指令改檔,因為那走的是 Bash 這個工具。另外可以在 Claude Code 裡打 /hooks,看它到底讀到了哪些 hook。

hook 可以用中文寫訊息嗎?

可以。我實測時擋下的理由寫中文,Claude 收到之後照著中文的理由回報給我。

這篇怎麼查證的

這篇只有一部分實際跑過,其餘依官方文件整理,查證日期 2026/10/14(在我自己的 Mac 上用 Claude Code 2.1.280 的非互動模式(claude -p)跑了三次,測試資料夾裡放一個待辦檔和一個假的 .env(沒有任何真的金鑰),hook 腳本是我寫的,只動這個資料夾裡的檔案。互動模式的 /hooks 選單、桌面通知、prompt 型和 http 型的 hook 沒有實測,依官方文件)。哪些有跑、哪些沒跑,文中各段有寫。價格與方案以官方頁面為準,工具改版後內容可能過期。