当前位置:首页 > 文章列表 > 文章 > python教程 > Python pathlib批量改名并保留冲突回滚点的脚本

Python pathlib批量改名并保留冲突回滚点的脚本

来源:17golang原创 2026-09-20 08:38:24 0浏览 收藏

整理一批导入文件时,直接在循环里调用 Path.rename() 看起来很快,但目标名一旦重复,脚本可能执行到一半才失败,留下“改了一部分、又不知道改了哪些”的现场。更稳妥的做法是把改名拆成计划、校验、执行和回滚四个阶段:先用 pathlib 生成确定性映射,冲突时整个计划不落地,成功项再写入可逆清单。

这个脚本只处理指定目录的直接文件,不递归进入子目录;它会先生成 rename-plan.json,发现目标已存在或目标重复就停止,执行成功后再生成 rename-rollback.json

先把源文件和目标文件变成一张计划表

Path 是面向当前操作系统的具体路径对象,适合同时完成目录遍历、名称拆分和重命名。计划表至少保留源路径、目标路径和是否已完成三个字段,后面检查冲突、写回滚清单都依赖它。

from pathlib import Path
import json

ROOT = Path("incoming")

def build_plan(root: Path) -> list[dict]:
    # 只处理当前目录的普通文件,避免一次误改名整个目录树。
    files = sorted((p for p in root.iterdir() if p.is_file()), key=lambda p: p.name)
    plan = []
    for index, source in enumerate(files, start=1):
        # 保留原后缀,只改变主文件名,目标顺序由排序后的源文件决定。
        target = source.with_name(f"asset-{index:03d}{source.suffix.lower()}")
        plan.append({
            "old": str(source),
            "new": str(target),
            "done": False,
        })
    return plan

plan = build_plan(ROOT)
Path("rename-plan.json").write_text(
    json.dumps(plan, ensure_ascii=False, indent=2), encoding="utf-8"
)

这里先排序再编号,意味着同一批文件重复运行时计划是稳定的。suffix.lower() 只统一扩展名大小写;如果业务需要严格保留原始后缀,应改为直接使用 source.suffix,不要把“规范化”悄悄混进改名规则。

pathlib批量改名计划结构说明图,展示源文件、目标名称和计划清单的关系
图1:批量改名计划说明图,展示源文件如何映射到确定性目标名;这是静态结构图,不是运行截图。

执行前同时拦住已存在目标和计划内重复

只检查 target.exists() 还不够:两个源文件也可能因为业务规则生成同一个目标名。将所有目标转成字符串集合后,分别检查“计划内重复”和“目录中已有目标”,任何一项命中都返回报告,不执行任何改名。

def find_conflicts(plan: list[dict]) -> list[str]:
    # 先找多个源文件指向同一目标,再找目录中已有的目标文件。
    conflicts = []
    seen: dict[str, str] = {}
    for item in plan:
        old_path = Path(item["old"])
        new_path = Path(item["new"])
        key = str(new_path)
        if key in seen:
            conflicts.append(f"计划重复: {seen[key]} -> {key} 与 {old_path}")
        else:
            seen[key] = str(old_path)
        # 源文件本身就是计划的一部分,不把它误判成外部冲突。
        if new_path.exists() and new_path != old_path:
            conflicts.append(f"目标已存在: {new_path}")
    return conflicts

conflicts = find_conflicts(plan)
if conflicts:
    for message in conflicts:
        print(message)
    raise SystemExit("计划存在冲突,未执行改名")

这个判断把“冲突”定义得很保守:即使目标是一个可以覆盖的临时文件,也先停下来让人确认。批处理脚本最怕默认覆盖,因为覆盖成功后往往无法分辨原文件属于哪一轮任务。

按计划执行,并记录每一条成功映射

冲突检查通过后,按计划表调用 rename()。每完成一条就把 done 写回计划文件,这样进程中断时可以知道最后完成的位置;同时只把已完成项放进回滚清单,避免回滚不存在的目标。

PLAN_FILE = Path("rename-plan.json")
ROLLBACK_FILE = Path("rename-rollback.json")

def save_json(path: Path, data: object) -> None:
    # 统一 UTF-8 写入,便于人工复核中文文件名和恢复记录。
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")

completed = []
for item in plan:
    old_path = Path(item["old"])
    new_path = Path(item["new"])
    try:
        # rename 保留同一文件系统内的原子改名语义,不复制文件内容。
        old_path.rename(new_path)
    except OSError as exc:
        # 已完成项仍然保留,下一次可先回滚或人工处理权限问题。
        save_json(ROLLBACK_FILE, list(reversed(completed)))
        raise RuntimeError(f"改名失败: {old_path} -> {new_path}: {exc}") from exc
    item["done"] = True
    completed.append({"old": str(old_path), "new": str(new_path)})
    save_json(PLAN_FILE, plan)

save_json(ROLLBACK_FILE, list(reversed(completed)))

清单使用“旧路径 → 新路径”的方向,回滚时则逆序执行“新路径 → 旧路径”。逆序的意义在于尽量先恢复后改名的文件,降低名称链条相互占用的概率;但它不是绕过外部文件占用的保证,回滚前仍要重新检查目标是否存在。

pathlib批量改名回滚结构说明图,展示成功映射、逆向清单和恢复边界
图2:回滚映射说明图,展示成功改名记录如何生成逆向恢复清单;这是静态结构图,不是运行截图。

回滚函数要拒绝覆盖新文件

回滚不是把清单无条件反向执行。恢复前要确认新路径仍存在、旧路径没有被别的文件占用,否则宁可报告失败,也不要用恢复动作覆盖用户后来创建的文件。

def rollback(records: list[dict]) -> None:
    for record in records:
        current = Path(record["new"])
        original = Path(record["old"])
        # 旧名重新出现时说明目录状态已变化,禁止覆盖。
        if not current.exists():
            raise FileNotFoundError(f"回滚源不存在: {current}")
        if original.exists():
            raise FileExistsError(f"回滚目标已被占用: {original}")
        current.rename(original)

# records = json.loads(ROLLBACK_FILE.read_text(encoding="utf-8"))
# rollback(records)  # 仅在确认目录状态后手动调用

几个容易被忽略的边界

场景处理建议
需要递归改名先明确是否允许跨目录移动,再用 rglob() 并重新设计排序和冲突策略。
符号链接默认不要跟随处理;把链接和真实文件分开列入计划,避免误改目标文件。
跨文件系统rename() 不等同于复制,跨设备时应改用复制、校验、删除的独立流程。
权限或文件被占用保留已完成清单,修复权限后先确认现场,再决定回滚还是从剩余计划继续。

最终核对不应只看文件数量,还要读取清单逐条确认旧路径已经不存在、新路径确实存在。只要清单和目录状态不一致,就先停在人工处理状态,不要再次运行整个批次。

相关问题

为什么不直接用字符串拼接文件路径?

Path 会按运行平台处理路径分隔符,并把名称、后缀、存在性和改名操作放在同一套对象接口中,减少手写分隔符和扩展名切分的错误。

目标文件已存在时能不能覆盖?

除非覆盖本身就是明确需求,并且有独立备份,否则不建议。对批量整理脚本来说,停止并输出冲突清单比静默覆盖更容易恢复。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go http.Response.Body读取后不关闭导致连接复用异常的排查方法Go http.Response.Body读取后不关闭导致连接复用异常的排查方法
上一篇
Go http.Response.Body读取后不关闭导致连接复用异常的排查方法
Go iter.Seq把回调序列接入for range的工程方式
下一篇
Go iter.Seq把回调序列接入for range的工程方式
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    129次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    198次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    143次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    118次使用
  • CMMLU中文大模型评估基准:功能、使用教程与应用场景解析
    CMMLU
    深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
    106次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码