批量文本查找替换
先说清楚要改什么、改在哪里,再动手,并且随时能还原。
触发规则
| 用户意图 | 处理 |
|---|---|
| 把这些文件里的 X 都改成 Y | 先预览,把结果给用户确认后再 --apply |
| 用正则批量替换 | 加 --regex,替换串可用 \1 引用捕获组 |
| 只改某类文件 | --include "*.py",或 --exclude "*.min.js" |
| 改错了要还原 | --restore <备份目录> |
| 只是问本技能能干嘛 | 仅介绍,不执行任何操作 |
依赖
| 依赖 | 用途 |
|---|---|
| Python 3.8+ | 仅标准库(re / fnmatch / shutil / pathlib) |
无需联网、无需第三方包。
快速开始
# 第一步:预览(不写任何文件)
python3 scripts/batch_replace.py ./src --find "old.example.com" --replace "new.example.com" --show-lines
# 第二步:确认后应用(自动备份)
python3 scripts/batch_replace.py ./src --find "old.example.com" --replace "new.example.com" --apply
# 正则 + 反向引用
python3 scripts/batch_replace.py ./src --regex --find 'v(\d+)\.(\d+)' --replace 'v\1.\2'
# 只改 Python 文件,排除测试
python3 scripts/batch_replace.py . --include "*.py" --exclude "*_test.py" --find "TODO" --replace "FIXME" --apply
# 还原
python3 scripts/batch_replace.py --restore ./.bak_replace
参数
| 参数 | 说明 | 默认 |
|---|---|---|
--find TEXT |
要查找的文本(或正则) | 必填 |
--replace TEXT |
替换为(正则模式下支持 \1 反向引用) |
空串 |
--regex |
--find 按正则解释 |
关 |
--ignore-case |
忽略大小写 | 关 |
--include GLOB |
仅处理匹配的文件(可重复) | 全部文本文件 |
--exclude GLOB |
排除匹配的文件(可重复) | — |
--encoding ENC |
强制编码 | 自动识别 |
--apply |
真正写回文件 | 关(只预览) |
--backup DIR |
备份目录 | --apply 时默认同级 .bak_replace |
--restore DIR |
从备份还原 | — |
--max-preview N |
每文件最多预览 N 处 | 5 |
--show-lines |
预览显示行号 | 关 |
自动跳过的内容
- 二进制扩展名(
.png.jpg.pdf.zip.xlsx.docx.exe.so等) - 版本控制与依赖目录:
.git.svnnode_modules__pycache__.venvvenv
distbuild.idea.vscode.bak_replace
编码与换行保真
| 情况 | 处理 |
|---|---|
| 文件有 UTF-8 BOM | 写回时保留 BOM |
| 文件无 BOM | 写回时不加 BOM |
| 文件是 GBK | 识别后按 GBK 写回 |
| 原文件 LF | 写回仍是 LF |
| 原文件 CRLF | 写回仍是 CRLF |
| 无法解码 | 跳过并在汇总中列出,不强行改写 |
退出码
| 码 | 含义 |
|---|---|
0 |
有命中 |
1 |
无命中(便于脚本判断) |
2 |
参数错误(如正则非法) |
Agent 执行要求
- 必须先预览:任何替换都先跑一次不带
--apply的预览,把命中文件数与处数报给用户,
得到确认再执行。 - 备份是默认动作:
--apply时必须让备份生效(不指定--backup也会用默认目录),
并在汇报中告知备份位置与还原命令。 - 不做静默改写:无法解码的文件必须列出来,不能悄悄跳过还说"全部完成"。
- 正则要说明范围:使用
--regex时把匹配到的内容展示给用户,避免过度匹配。 - 排除目录要交代:让用户知道
.git、node_modules等目录默认被跳过。 - 不建议无预览批改整个盘:路径过宽(如用户主目录)时先收窄范围,或先
--max-preview抽样。
输出模板
## 批量替换
模式:预览(未写文件)
查找:'old.example.com' → 'new.example.com'
待扫描文件:128 个
--- src/config/a.conf(2 处)---
L1: - host = "old.example.com"
+ host = "new.example.com"
L3: - name = "old.example.com"
+ name = "new.example.com"
命中文件:2 个 总替换处数:4
跳过(无法解码):1 个,例如 ./legacy/blob.dat
提示:这是预览,未修改任何文件。确认后加 --apply 执行。
注意事项
- 禁止在未预览的情况下对宽范围路径执行
--apply。 - 「预览 → 应用 → 还原」流程中,还原依赖备份目录里的
_manifest.tsv清单,
不要手工删除该文件。 - 不做语义级重构(如重命名变量并同步所有引用),也不解析语法结构。
- 不处理压缩包内部、数据库内的文本。