Files
claude-code/docs/tools/file-operations.mdx
claude-code-best 29859a84ff docs: 重写文件操作,从源码解剖改为工具设计分析
移除 TypeScript 代码、源码路径和实现常量,
聚焦风险分级设计、引号标准化的AI能力边界补偿、
原子性读改写的竞态防御和行尾处理的智能陷阱教训。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 10:48:20 +08:00

122 lines
5.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "文件操作"
description: "Read/Edit/Write 三个工具不是功能划分,而是风险分级。理解读取去重、原子性编辑、文件历史快照和安全防线的设计。"
keywords: ["文件操作", "FileRead", "FileEdit", "FileWrite", "代码编辑", "原子写入"]
---
## 核心设计:风险分级
Claude Code 将文件操作拆分为三个独立工具——这不是功能划分,而是**风险分级**
| 工具 | 风险级别 | 典型场景 |
|------|---------|----------|
| **Read** | 只读(免审批) | 查看代码、搜索内容 |
| **Edit** | 写入(需确认) | 修改已有代码 |
| **Write** | 写入/创建(需确认) | 创建新文件、全量重写 |
拆成三个工具让权限系统可以精确控制:只读模式只禁用 Edit/Write允许 AI 自由探索代码;而全禁用模式则连 Read 都受限。
## Read多模态读取引擎
Read 工具不只是一个 `cat` 命令。它是一个多格式分发器:
| 文件类型 | 处理路径 | 特殊处理 |
|---------|---------|---------|
| 文本文件 | 分页读取 | 支持行号范围 |
| 图片 | 压缩 + 降采样 | 自动调整到 token 预算内 |
| PDF | 页面级提取 | 超大 PDF 强制分页读取 |
| Notebook | JSON cell 解析 | 保留 cell 结构 |
### 读取去重
当 AI 重复读取同一个文件时系统通过文件修改时间mtime比对避免重复发送相同内容。约 18% 的 Read 调用是重复读取——去重机制直接节省了这部分 token 开销。
**设计细节**:去重只对 Read 工具自身的读取生效。Edit/Write 也会更新内部状态,但不会误触发去重——通过 offset 字段区分读取来源。
### 安全防线
Read 工具有多层安全门:
- **设备文件屏蔽**`/dev/zero`、`/dev/random` 等被直接拒绝——它们会产生无限输出或阻塞
- **二进制文件拒绝**:排除图片/PDF 后,`.exe`、`.so` 等二进制文件被阻止
- **UNC 路径跳过**Windows 下 `\\server\share` 路径跳过操作,防止 SMB 凭据泄露
### 智能错误提示
文件不存在时Read 不只是报错——它会尝试提供修复建议:
- 相似文件名的推荐
- 基于 CWD 的相对路径建议
- macOS 截图文件名中特殊空格字符的纠正
## Edit精确字符串替换
Edit 工具的核心操作是"找到旧字符串,替换为新字符串"。听起来简单,实际充满了边缘情况。
### 引号标准化
AI 模型只能输出直引号(`'` `"`),但源码中可能使用弯引号(`'` `'` `"` `"`。Edit 工具在匹配时自动标准化引号,但写入时保持文件原有的引号风格——如果文件用弯引号,替换后的新内容也用弯引号。
**设计洞察**:这是一个典型的"AI 能力边界补偿"设计。AI 的输出限制(只能用直引号)不应该成为文件修改的问题。系统在 AI 和文件系统之间做了透明翻译。
### 原子性读-改-写
Edit 的执行过程是一个无锁原子更新协议:
```
备份旧内容 → 同步读取 → mtime 校验 → 查找匹配 → 计算 diff → 写入磁盘 → 更新缓存
```
**关键约束**:从"同步读取"到"写入磁盘"之间不允许任何异步操作。这确保在 mtime 校验和实际写入之间不会有其他进程修改文件——否则就会出现"读到的内容和写入时的内容不一致"的竞态条件。
### 防覆写校验
Edit 前置条件:
1. **必须先读取**文件AI 不能编辑没看过的文件)
2. **文件未被外部修改**mtime 未变)
Windows 上的 mtime 可能因云同步或杀毒软件被修改而不改变内容,因此对全量读取做了内容级比对作为兜底。
## Write全量写入与创建
Write 与 Edit 共享大部分基础设施权限检查、mtime 校验、历史备份),但有两个关键差异。
### 行尾处理
Write 始终使用 LF 行尾。早期版本会保留旧文件的行尾风格,但这导致 Linux 上 bash 脚本被注入 `\r`——现在 AI 发什么行尾就用什么行尾,不再尝试"智能"转换。
**设计教训**:有时"不做智能处理"比"做智能处理"更安全。
### 创建 vs 更新
Write 返回操作类型:
- **create**:文件不存在,全新创建
- **update**:文件存在且被覆盖,包含完整 diff
这让用户和 AI 都能清楚知道操作的实际影响。
## 文件历史快照
每次 Edit/Write 前都会备份旧内容。快照系统最多保留 100 个版本,使用内容哈希去重(同一文件多次未变只存一份)。
**设计目的**:不是版本控制(那是 git 的工作),而是"撤销"功能——用户可以在会话中回退 AI 的任何文件修改。
## LSP 通知链路
Edit 和 Write 完成后会通知 LSP 服务器和 IDE 扩展:
1. 清除旧的诊断信息
2. 通知 LSP 文件已变更
3. 触发 LSP 重新计算诊断(如 TypeScript 类型检查)
4. 通知 IDE 更新 diff 视图
这确保文件修改后 IDE 端的实时反馈是同步的——AI 改了一个文件TypeScript 的类型错误立刻出现在编辑器中。
## 安全提醒
Read 工具在读取文件内容后追加安全提醒如果文件看起来像恶意代码AI 应该分析但拒绝改进。这是在"帮助用户"和"防止滥用"之间的平衡。
## 接下来
- **搜索与导航** — Glob/Grep 的搜索策略
- **Shell 执行** — Bash 的沙箱和超时控制
- **权限模型** — 理解工具权限的完整设计