批量重命名:去"(1)"后缀、空格换下划线,两阶段改名防互相覆盖

2026-10-06 · 喵窝资源铺

场景:下载文件夹 / 微信接收目录里几百个 未命名文档(1).docx、photo (1).jpg、IMG_2024 bak.jpg。Windows 自带 F2 一个个改太慢;网上脚本大多直接 os.rename 一把梭——这篇的版本默认预览、加 --go 才执行。文中代码实跑验证,真实输出贴在下面。

脚本做的事

输入处理
(1) (2) 下载重复后缀去掉扩展名前的 (N);撞名自动加 _2 _3
文件名里的空格压缩多余空格并换成下划线(--nounderscore 可关)
安全默认 dry-run 只打印改名计划;--go 才执行;两阶段改名防互相覆盖

完整脚本

#!/usr/bin/env python3
"""批量重命名:默认 dry-run 预览,--go 才真正执行。
用法: python3 rename.py 目录 [--go] [--nounderscore]
"""
import argparse, os, re, sys

def plan(d, underscore=True):
    renames, used = [], set(os.listdir(d))
    for name in sorted(os.listdir(d)):
        if name == os.path.basename(__file__): continue
        new = re.sub(r'\s*\(\d+\)(?=\.[^.]+$)', '', name)   # 去掉扩展名前的 (1)
        new = re.sub(r'\s+', ' ', new).strip()              # 压缩空格
        if underscore: new = new.replace(' ', '_')
        if new == name: continue
        base, ext = os.path.splitext(new); cand, i = new, 1
        while cand in used and cand != name:
            i += 1; cand = f"{base}_{i}{ext}"
        used.discard(name); used.add(cand)
        renames.append((name, cand))
    return renames

if __name__ == '__main__':
    ap = argparse.ArgumentParser()
    ap.add_argument('dir'); ap.add_argument('--go', action='store_true')
    ap.add_argument('--nounderscore', action='store_true')
    a = ap.parse_args()
    ren = plan(a.dir, not a.nounderscore)
    if not ren: print('无需重命名'); sys.exit(0)
    for old, new in ren: print(f'{old}  ->  {new}')
    if a.go:
        # 先全部改临时名再改最终名,避免 A->B 与 B->C 互相覆盖
        tmp = [(o, f'__tmp__{i}') for i, (o, _) in enumerate(ren)]
        for o, t in tmp: os.rename(os.path.join(a.dir, o), os.path.join(a.dir, t))
        for (_, t), (_, n) in zip(tmp, ren): os.rename(os.path.join(a.dir, t), os.path.join(a.dir, n))
        print(f'已重命名 {len(ren)} 个文件')
    else:
        print('(预览模式,确认无误后加 --go 执行)')

实跑输出(真实)

测试目录放 5 个典型脏文件名:

IMG_2024 bak.jpg  ->  IMG_2024_bak.jpg
photo (1).jpg  ->  photo.jpg
photo (2).jpg  ->  photo_2.jpg
未命名文档(1).docx  ->  未命名文档_2.docx

加 --go 后 ls 复核:IMG_2024_bak.jpg、photo.jpg、photo_2.jpg、未命名文档.docx、未命名文档_2.docx——与预览完全一致。注意 未命名文档(1) 撞上已有的 未命名文档,自动落成 未命名文档_2 而不是覆盖。

实跑抓到的两个真坑

  1. A→B 和 B→C 顺序执行会互相覆盖。目录里同时有 photo.jpg 和 photo (1).jpg 时,改名计划是 photo(1)→photo(冲突让它落 _2 才安全);但更一般的场景:计划里有 a→b 和 b→c,若按列表顺序直接 os.rename,先执行 a→b,原 b 还没来得及改成 c 就被覆盖,文件被吞且无任何报错。解法是两阶段:先全部改成 __tmp__0/1/2...,再从临时名改成最终名。
  2. 重名冲突判断要维护一个动态 used 集合。只和磁盘现状比对不够:本轮改名自己产生的 photo_2.jpg,会被后处理的另一个 photo (2).jpg 撞上。所以 used 集合每落定一个新名字都要 add、腾出的旧名字 discard,冲突检测才闭环。

顺手提一句

清理完文件名,下一步往往是统一文档格式/加页码/批量转 PDF——这些都有实跑过的教程:Word 批量转 PDF、PDF 批量加页码、Word 批量查找替换。不想装环境折腾?把文件发给我们代做(¥9.9 起,做不对免费重做;虚拟物品交付后不退款)。

← 返回全部文章 · 喵窝资源铺·文档代做服务