场景:老板发来一份 Excel,A列是旧文件名、B列是规范后的新文件名,让你把文件夹里几百个"报告_旧版1.docx、名单(3)(最终).xlsx"批量改成规范名字。手动改到手抽筋,这篇给你一个实测可用的脚本。
#!/usr/bin/env python3
# 用法: python3 rename_by_excel.py map.xlsx ./docs [--dry-run]
import csv, os, sys, argparse
def read_map(xlsx):
import openpyxl
wb = openpyxl.load_workbook(xlsx, read_only=True)
ws = wb.active
rows = [(str(r[0]).strip(), str(r[1]).strip())
for r in ws.iter_rows(values_only=True)
if r and len(r) > 1 and r[0] and r[1]]
wb.close()
if rows and any(k in rows[0][0] for k in ("旧","原","old")):
rows = rows[1:] # 跳过表头
return rows
ap = argparse.ArgumentParser()
ap.add_argument("xlsx"); ap.add_argument("folder")
ap.add_argument("--dry-run", action="store_true")
a = ap.parse_args()
rows = read_map(a.xlsx)
seen, log = set(), []
for old, new in rows:
if old in seen:
log.append((old, new, "duplicate_skipped")); continue
seen.add(old)
src = os.path.join(a.folder, old)
if not os.path.exists(src):
log.append((old, new, "missing")); continue
dst = os.path.join(a.folder, new)
n = 1; base, ext = os.path.splitext(new)
while os.path.exists(dst): # 撞名自动加序号,绝不覆盖
dst = os.path.join(a.folder, f"{base}({n}){ext}"); n += 1
if not a.dry_run: os.rename(src, dst)
log.append((old, os.path.basename(dst), "renamed"))
with open("rename_log.csv", "w", newline="", encoding="utf-8-sig") as f:
w = csv.writer(f); w.writerow(["旧文件名","实际新文件名","结果"]); w.writerows(log)
print("完成,明细见 rename_log.csv(交换两列再跑一遍即可撤销)")
我在本机构造了真实测试用例,包括三种最容易翻车的边界情况:
| 测试场景 | 脚本行为 | 结果 |
|---|---|---|
| 3 个正常改名 | 全部成功 | ✅ 报告_旧版1.docx → 2026Q3_财务报告.docx 等 |
| 映射表里写了不存在的旧文件 | 记入 missing,不报错不中断 | ✅ 其余文件照常处理 |
| 同一旧文件名出现两次 | 只执行第一次,第二次标记 duplicate | ✅ 防止二次改名把文件改乱 |
| 新文件名已存在(撞名) | 自动加 (1) 序号,绝不覆盖 | ✅ 员工名册_10月(1).xlsx,原文件完好 |
| --dry-run 预演 | 只打印不改动 | ✅ 先看效果再动手 |
← 返回全部文章 | 2026-10-06