Claude Codeで必ず高速なGrep,Globツールを使うようにして低速なfind/grepコマンドを実行しないようにする

ブログ
claude-code-fast-grep.avif

Claude Codeが低速な grep/find コマンドを使ってしまう問題

hooks→PreToolUse / hooks→PostToolUse でツールの使用具合を監視する

settings.json に以下の設定を追加し続けて次の2つのファイルを新規作成します

~/.claude/settings.json

{
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "\"$HOME\"/.claude/hooks/log-tool-usage.sh"
        }
      ]
    }
  ],
  "PostToolUse": [
    {
      "matcher": "Grep|Glob",
      "hooks": [
        {
          "type": "command",
          "command": "\"$HOME\"/.claude/hooks/log-fast-tool-usage.sh"
        }
      ]
    }
  ]
}

~/.claude/hooks/log-fast-tool-usage.sh

#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')


LOG="$HOME/.claude/logs/search-tool-usage.jsonl"
mkdir -p "$(dirname "$LOG")"


jq -nc --arg ts "$(date -u +%FT%TZ)" --arg tool "$TOOL" \
  '{ts:$ts, type:"fast", tool:$tool}' >> "$LOG"
exit 0

~/.claude/hooks/log-tool-usage.sh

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')


LOG="$HOME/.claude/logs/search-tool-usage.jsonl"
mkdir -p "$(dirname "$LOG")"


if echo "$COMMAND" | grep -Eq '(^|[;&|]\s*)(grep|egrep|fgrep|find)\b'; then
  jq -nc --arg ts "$(date -u +%FT%TZ)" --arg cmd "$COMMAND" \
    '{ts:$ts, type:"slow", tool:"Bash", command:$cmd}' >> "$LOG"
fi


exit 0

このファイルを設置すると、クロードコードでツールを実行するたびに、次の箇所にログがたまります。
ここに findgrep などの遅いツールが使われているかどうかをまず確認します。

もし使われているようなら次の工程でそれらを高速ツールに置き換えましょう

Claude Codeに作業を任せていて、少し気になったことがありました。

「これまでの作業でfindやgrepを直接使ったか」と聞くと、かなりの確率でBash経由の遅いfind/grepを使っていることがわかりました。

Claude Codeには元々ripgrepベースの高速なGrepツールとGlobツールが組み込まれているのですが、これらは使われず、素のfind/grepコマンドが実行されてしまいます。


なぜ遅いコマンドが選ばれてしまうのか

理由は単純で、Claude Codeはgrepfind組み込みのread-onlyコマンドとして扱っており、Bash経由でも許可プロンプトなしにそのまま実行できてしまいます。

一方、専用のGrep/Globツールは.gitignoreを尊重し、globtypeで絞り込めるなど本来はこちらの方が高速かつ的確なのですが、選ばせない仕組みが特にないため、習慣的にBash経由のコマンドが選ばれてしまう、という状態でした。

CLAUDE.mdに「Grepツールを使うこと」と書くだけでは弱いです。

パーミッションルールやhookはClaude Code自体が強制するものであり、CLAUDE.mdの指示はあくまで「促す」だけで「強制」はしない

とのことなので、確実に矯正するにはPreToolUseフックでBash経由のfind/grepをブロックし、正しいツールへ誘導する方法を取ることにしました。


フックスクリプトを作る

.claude/hooks/prefer-fast-search.shを作成します。

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if echo "$COMMAND" | grep -Eq '(^|[;&|]\s*)(grep|egrep|fgrep|find)\b'; then
  echo "grep/find ではなく、組み込みの Grep ツール(ripgrep ベース)または Glob ツールを使ってください。" >&2
  exit 2
fi
exit 0

実行権限を付けます。

chmod +x .claude/hooks/prefer-fast-search.sh

.claude/settings.jsonに登録します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/prefer-fast-search.sh"
          }
        ]
      }
    ]
  }
}

動作の仕組み

     Bashツール呼び出し
           │
           ▼
   grep/find を含む?
   │             │
  Yes            No
   │             │
   ▼             ▼
ブロック(exit 2)   そのまま実行
理由をClaudeへ返す
   │
   ▼
Grep/Globツールへ
切り替えて再試行

ポイントはexit 2です。stderrに書いたメッセージがそのままClaudeへのフィードバックとして返るため、ブロックされた瞬間に「正しいツールを使うべきだ」とClaudeが認識し、切り替えてくれます。

permissions.denyでBashのgrep/find自体を禁止する方法も考えましたが、この場合Claudeに返るのは単なる「拒否されました」だけで、代替手段が示されません。フックなら理由を添えられる分、こちらの方が確実でした。


料金への影響は?

フック追加によるコスト増はあるのか?

  • type: "command"のフックはただのシェルスクリプト実行であり、モデル呼び出しを伴わないため、フック自体には課金が発生しない
  • ブロック時に拒否理由が会話履歴に載るため、その分だけ多少コンテキストは増える(数十トークン程度)
  • とはいえ、非効率なfind/grepの大きな出力を毎回読み込むコストと比べれば誤差程度で、セッションが進むほどClaudeが学習して最初からGrep/Globを選ぶようになるため、トータルではむしろ削減方向に働く

type: "prompt"type: "agent"のフックは判断のために別途モデルを呼ぶため課金対象ですが、今回のような単純なコマンドチェックにはcommandタイプで十分でした。

一覧へ戻る
Claude Codeで必ず高速なGrep,Globツールを使うようにして低速なfind/grepコマンドを実行しないようにする - ecos(エコノシス システム設計事務所)