课程大纲 › 领域 2:工具设计与 MCP

2.5 有效选择并运用内置工具(Read、Write、Edit、Bash、Grep、Glob)

タスクステートメント2.5:組み込みツール(Read、Write、Edit、Bash、Grep、Glob)を効果的に選択し適用する

官方大纲原文(日文):知识 4 条 · 技能 5 条

需要掌握的知识

  • コンテンツ検索のためのGrep(関数名、エラーメッセージ、import文などのパターンをファイルの内容から検索する)
  • ファイルパスのパターンマッチングのためのGlob(名前や拡張子のパターンでファイルを見つける)
  • ファイル全体の操作のためのRead/Write。一意のテキストマッチングを使用した的を絞った変更のためのEdit
  • テキストの一致が一意でないためにEditが失敗した場合に、確実なファイル変更のためのフォールバックとしてRead + Writeを使用すること

需要能做到的事

  • コードベース全体でコードの内容を検索するためにGrepを選択する(例:ある関数のすべての呼び出し元を見つける、エラーメッセージの場所を特定する)
  • 命名パターンに一致するファイルを見つけるためにGlobを選択する(例:**/*.test.tsx)
  • Editが一意のアンカーテキストを見つけられない場合に、Readでファイルの内容全体を読み込んでからWriteを使用する
  • 最初にすべてのファイルを読むのではなく、Grepでエントリーポイントを見つけることから始め、次にReadでimportをたどってフローを追跡することで、コードベースの理解を段階的に構築する
  • まずエクスポートされたすべての名前を特定し、次にコードベース全体で各名前を検索することで、ラッパーモジュールをまたぐ関数の使用箇所を追跡する

课程讲解 · AI 草稿,未经人工审核 以官方文档为准

这一条考什么

考你能否针对具体任务选对 Claude Code / Agent SDK 的内置工具:搜内容用 Grep,按文件名找文件用 Glob,整文件读写用 Read/Write,局部精确修改用 Edit;Edit 因匹配不唯一失败时知道用 Read + Write 兜底。更高阶的是“探索策略”:先用 Grep 找入口再沿 import 逐步阅读,以及追踪经过包装模块改名后的函数调用方。

核心概念

Grep:搜索文件“内容”

Grep 基于正则在文件内容中查找匹配行,返回文件路径、行号和上下文。适用场景:

  • 找某个函数的所有调用方:processRefund\(
  • 定位错误信息出自哪里:日志里的 SYNC_CONFLICT、entity version mismatch
  • 找所有导入某个包的文件:from ['"]@company/auth
  • 安全扫描危险调用:eval\(

判断关键词:题目说的是“包含某段文本 / 调用 / import / 报错信息”的文件,就是 Grep。

Glob:匹配文件“路径”

Glob 按路径模式找文件,不看内容。适用场景:

  • 所有测试文件:**/*.test.tsx
  • 某目录下的所有配置:config/**/*.yaml
  • 名字里带 migration 的文件:**/*migration*

判断关键词:题目说的是“文件名 / 扩展名 / 目录结构符合某模式”,就是 Glob。用 Glob 找 **/eval* 来找 eval 调用是典型错误——文件名里没有 eval 的文件里同样可能调用 eval。

Read / Write / Edit

  • Read:读取整个文件(可指定行范围),是理解代码、编辑前确认内容的基础。
  • Write:整文件写入或覆盖,适合新建文件或彻底重写。
  • Edit:用 old_string → new_string 做精确替换。要求 old_string 在文件中唯一匹配,否则失败(除非显式使用 replace_all 替换所有出现处)。Edit 的优点是改动小、可审查、不会误伤其他部分。
{
  "name": "Edit",
  "input": {
    "file_path": "/repo/src/booking.py",
    "old_string": "def validate_booking(booking):\n    if booking.guests < 1:",
    "new_string": "def validate_booking(booking):\n    if booking.date <= date.today():\n        raise ValueError(\"date must be in the future\")\n    if booking.guests < 1:"
  }
}

Edit 失败时:Read + Write 兜底

文件里充满重复的 docstring、相同的变量名和结构,找不到唯一锚点文本时,Edit 会反复失败。可靠的兜底方案:用 Read 读入整个文件,在完整内容中确定插入位置,再用 Write 写回完整文件。 不要用 replace_all 去替换一个常见模式(会改动所有出现处),也不要用 Bash heredoc 追加到文件末尾(位置不对,且绕开了编辑工具的追踪)。在可行时,先尝试扩展 old_string 加入足够的上下文使其唯一,也是合理的第一步;但大纲强调的兜底是 Read + Write。

增量式理解代码库

不要一上来就把所有文件 Read 一遍——那会迅速耗尽上下文,而且大部分内容无关。推荐流程:

  1. 用 Grep 找到入口点(路由定义、main 函数、报错文本、关键函数名)。
  2. Read 入口文件,看它 import 了什么。
  3. 沿 import 链有选择地 Read 下一层文件,逐步构建流程图。
  4. 需要时再用 Grep 横向确认“还有谁用了它”。

追踪经过包装模块的函数

库函数 calculateTax 被 orders 模块包装成 computeOrderTax 再导出,只 Grep 原名会漏掉通过别名调用的地方。可靠做法:先 Read 库模块和包装模块,列出该函数对外暴露的所有名字(原名、re-export、别名),再对每个名字在全仓库 Grep。 只 Grep 所有 import 了库的文件再逐个阅读,代价高且仍可能漏掉二次包装。

# 第一步:找出所有导出名
grep -rn "calculateTax" src/lib src/orders/index.ts
# 发现:export const computeOrderTax = calculateTax
# 第二步:对每个名字搜索调用方
grep -rn -E "calculateTax\(|computeOrderTax\(" src/

Bash 的定位

Bash 很强大,但对于内容搜索和文件查找,专用的 Grep/Glob 更合适:输出结构化、权限更好控制、不依赖平台命令差异。ls -R | grep eval 只搜文件名,find -name "*auth*" 只按名字找目录,都无法回答“谁在代码里用了它”。

正确做法与反模式

  • 正确:用 Grep 找某函数的所有调用方、定位报错信息来源。反模式:Read README 和配置后逐个服务读源码;用 Glob 按“errors/exceptions”目录名猜位置。
  • 正确:用 Glob **/*.test.tsx 找所有测试文件。反模式:用 Grep 搜内容来间接判断哪个文件是测试文件。
  • 正确:Edit 找不到唯一锚点时,Read 全文再 Write 写回。反模式:replace_all 替换常见模式、Bash 追加到文件末尾。
  • 正确:Grep 找入口,Read 沿 import 逐步追踪。反模式:一开始就把整个代码库 Read 进上下文。
  • 正确:先识别包装模块导出的所有名字,再逐个 Grep。反模式:只 Grep 原函数名,漏掉别名调用。

常见陷阱

  • 把“内容”问题当成“文件名”问题:例如用 Glob **/*auth* 找导入 @company/auth 的文件。导入语句在文件内容里,文件名不一定含 auth。
  • “从 package.json / README 开始读”:听起来系统,实则低效,题目问“最有效”时几乎总是 Grep 一段独特文本。
  • Grep 一个“模块”而不是具体文本:如 Grep 所有导入错误处理模块的文件再逐个阅读,范围过大;应直接 Grep 报错信息中的独特字符串。
  • Edit 失败后的花式补救:replace_all、Bash heredoc、手工拼超长正则,都可能改错位置。记住大纲给出的兜底:Read + Write。
  • 只搜原始函数名:在有包装/重命名的题干里,这个选项是“看起来对但不完整”的陷阱。
  • 用 Bash `ls -R | grep` 或 `find` 代替 Grep:只匹配路径,不匹配内容。

实操练习

目标:用 Claude Code 的无头模式,观察模型在受限工具集下如何选择 Grep/Glob/Read,并体验 Edit 的唯一性约束。

  1. 准备一个小仓库:src/lib/tax.ts 导出 calculateTax,src/orders/index.ts 中 export const computeOrderTax = calculateTax,另有两三个文件分别调用这两个名字;再建几个 *.test.tsx 文件。
  2. 只开放只读工具,让 Claude 找出所有调用方。
  3. 查看输出中它是否先读取包装模块再按多个名字搜索。
  4. 再让它在一个满是重复 docstring 的文件中插入新函数,观察 Edit 是否失败并改用 Read + Write。
claude -p "找出仓库中所有调用 calculateTax 的地方,包括通过包装模块改名后的调用。先说明你识别出的所有导出名,再列出 文件:行号。" \
  --allowedTools "Read,Grep,Glob" \
  --output-format json | jq -r '.result'

claude -p "列出所有 .test.tsx 文件的路径" --allowedTools "Glob" --output-format text

检查结果:第一条命令的结果应同时包含 calculateTax 和 computeOrderTax 的调用位置;第二条应只用 Glob 就返回测试文件列表。若想看工具调用过程,可改用 --output-format stream-json --verbose 观察每次 tool_use 的工具名。

自测清单

  • “找出所有调用 eval() 的位置”和“找出所有 *.spec.ts 文件”分别用什么工具?为什么不能互换?
  • Edit 工具对 old_string 有什么要求?失败时可靠的兜底方案是什么?
  • 为什么不应该在理解代码库时先 Read 所有文件?推荐的增量流程是怎样的?
  • 函数经过包装模块改名导出时,怎样确保找到所有调用方?
  • 日志里出现一条陌生报错,不知道来自哪个服务,最快的定位方法是什么?

对应题目(5)

题目归类由 AI 判断,仅供参考。

练习这 5 道题