编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • ScriptHub 脚本工具箱
  • Python

  • Shell-Bash

  • 工具脚本

    • 工具脚本速查
    • 哈希校验
    • Base64编码
    • AES加解密
    • RSA签名验签
    • JWT令牌
    • JSON与YAML
    • XML与CSV
    • 编码转义
    • 图片转换
    • 文档转换
    • 批量重命名
      • 文件系统底层原理
      • 一、Python 批量重命名
        • 1.1 基础模式:正则替换 + 前缀/后缀
        • 1.2 实用模式:序号补零 / 删除字符 / 扩展名替换
        • 1.3 按日期信息改名
      • 二、CLI 工具:防误操作的 dry-run
      • 三、Shell 方案
    • 分割合并
    • 目录同步
    • 文件监控
    • 压缩归档
    • 文件去重
    • cURL速查
    • HTTP调试
    • 端口DNS
    • 抓包代理
  • ScriptHub
  • 工具脚本
杨充
2021-11-27
目录

批量重命名

# 批量重命名

正则替换、添加前缀/后缀、序号补零、按日期改名——Python + Shell 双方案,dry-run 防误操作。

# 文件系统底层原理

在深入脚本之前,先理解「重命名」在操作系统层面到底做了什么:

os.rename(old, new) 调用的是 rename() 系统调用。 它不移动文件数据——只在目录元数据中修改文件名指向同一个 inode(索引节点)。因此在同一文件系统内,重命名是原子操作且速度极快(O(1) 时间复杂度),与文件大小无关。跨文件系统重命名则退化为"复制+删除"——os.rename 会自动失败,需改用 shutil.move。

inode 解释:Linux 文件系统中,文件名和数据是分离的。文件名存在目录中(目录本身也是文件),指向一个 inode;inode 存储文件的实际元数据(大小、权限、时间戳、数据块位置)。改文件名 = 修改目录条目中的 inode 指针——完全不触碰文件数据。

pathlib.Path 是 Python 3.4+ 引入的面向对象路径库。相比 os.path 的字符串拼接方式,p.with_name() / p.with_suffix() / p.rename() 等方法链式可读,且自动处理不同操作系统的路径分隔符。

# 一、Python 批量重命名

# 1.1 基础模式:正则替换 + 前缀/后缀

Path.iterdir() 返回目录下所有条目的迭代器(惰性、不预加载全部文件名)。p.with_name(new) 替换文件名部分而保留路径前缀——这是保证重命名不会意外移动文件的关键。

#!/usr/bin/env python3
"""批量重命名——核心:rename() 原子系统调用"""
import os, re
from pathlib import Path

def rename_by_pattern(directory, pattern, replacement):
    """正则替换文件名——(group) 捕获组在 replacement 中可用 \\1 引用"""
    for p in Path(directory).iterdir():
        if p.is_file():
            new_name = re.sub(pattern, replacement, p.name)
            if new_name != p.name:
                p.rename(p.with_name(new_name))
                print(f"  {p.name} → {new_name}")

def add_prefix(directory, prefix):
    """批量添加前缀——常用于标记备份文件"""
    for p in Path(directory).iterdir():
        if p.is_file():
            p.rename(p.with_name(f"{prefix}{p.name}"))
            print(f"  → {prefix}{p.name}")

def add_suffix(directory, suffix):
    """添加后缀但保留扩展名——p.stem(去掉扩展名) + suffix + p.suffix(扩展名)"""
    for p in Path(directory).iterdir():
        if p.is_file():
            new_name = f"{p.stem}{suffix}{p.suffix}"
            p.rename(p.with_name(new_name))
            print(f"  → {new_name}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

为什么用 p.stem 和 p.suffix? Path.name = 完整文件名 "my_file_v2.tar.gz";Path.stem = "my_file_v2.tar";Path.suffix = ".gz"。理解这个拆分才能准确地"在扩展名之前插入"——许多新手直接在 p.name 前面插字符串,结果后缀变成了 .jpg_v2。

# 1.2 实用模式:序号补零 / 删除字符 / 扩展名替换

序号补零的数学依据:如果文件总数是 N,补零位数 = ⌈log₁₀(N+1)⌉。例如 150 个文件需要 3 位(001-150)。代码中 len(str(len(files))) 即实现此计算。

#!/usr/bin/env python3
import os
from pathlib import Path

def number_files(directory, name_format="file_{:03d}"):
    """按名称排序编号——先 sorted 保证顺序一致,自动按文件数量补零"""
    files = sorted(Path(directory).iterdir(), key=lambda p: p.name)
    for i, p in enumerate(files, 1):
        if p.is_file():
            new = p.with_name(f"{name_format.format(i)}{p.suffix}")
            p.rename(new)
            print(f"  {p.name} → {new.name}")

def remove_chars(directory, chars):
    """批量删除文件名中的指定字符——空格、特殊字符清洗"""
    for p in Path(directory).iterdir():
        new_name = p.name
        for ch in chars:
            new_name = new_name.replace(ch, '')
        if new_name != p.name:
            p.rename(p.with_name(new_name))
            print(f"  {p.name} → {new_name}")

def replace_extension(directory, old_ext, new_ext):
    """批量替换扩展名: .jpeg → .jpg——注意大小写!"""
    old, new = f'.{old_ext.lstrip(".")}', f'.{new_ext.lstrip(".")}'
    for p in Path(directory).glob(f'*{old}'):
        p.rename(p.with_suffix(new))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 1.3 按日期信息改名

os.path.getmtime() 返回的修改时间是一个 Unix 时间戳(自 1970-01-01 起的秒数,浮点型)。datetime.fromtimestamp() 将其转为可读格式。需要注意:mtime 在复制文件时通常保留源文件时间(取决于复制工具)——所以下载的照片可能仍显示拍摄时间。

#!/usr/bin/env python3
from pathlib import Path
from datetime import datetime
import os

def rename_by_mtime(directory, date_format="%Y%m%d_%H%M%S"):
    """按文件修改时间改名,存在同名文件时自动跳过"""
    for p in Path(directory).iterdir():
        if p.is_file():
            mtime = os.path.getmtime(p)
            date_str = datetime.fromtimestamp(mtime).strftime(date_format)
            new = p.with_name(f"{date_str}{p.suffix}")
            if not new.exists():
                p.rename(new)
                print(f"  → {new.name}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 二、CLI 工具:防误操作的 dry-run

设计哲学:批量重命名是不可逆操作——没有"撤销重命名"。因此 CLI 工具默认只预览、不执行(dry-run 模式),加 -x 才真正改名。这是所有批量操作工具的核心安全设计。

#!/usr/bin/env python3
import sys, re
from pathlib import Path

def batch_rename(directory, pattern, replacement, execute=False):
    files = sorted(Path(directory).glob('*'))
    renamed = 0
    for p in files:
        if not p.is_file(): continue
        new_name = re.sub(pattern, replacement, p.name)
        if new_name != p.name:
            new_path = p.with_name(new_name)
            if execute:
                p.rename(new_path)
            ren = '✅' if execute else '🔍'
            print(f"  {ren} {p.name} → {new_name}")
            # 预检查:目标文件是否已存在——存在则 rename 会覆盖它
            if new_path.exists() and not execute:
                print(f"     ⚠️  目标已存在,将跳过")
            renamed += 1
    print(f"{'已' if execute else '预览——'}改名 {renamed} 个文件,加 -x 确认执行")

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print("用法: rename.py <目录> <正则模式> <替换> [-x]")
        sys.exit(1)
    batch_rename(sys.argv[1], sys.argv[2],
                 sys.argv[3] if len(sys.argv)>3 else '',
                 '-x' in sys.argv)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 三、Shell 方案

Shell 的 mv 同样调用 rename() 系统调用。rename 命令(Perl 版)在处理大量文件时比 for+mv 快,因为它对通配符展开和循环做了内部优化。

#!/bin/bash

# ===== rename (Perl 版)——正则重命名,内部用 eval 批量执行 =====
rename 's/IMG_/photo_/' *.jpg            # IMG_001→photo_001
rename 's/ +/_/g' *                       # 空格→下划线(冒泡排序式遍历)
rename 'y/A-Z/a-z/' *                     # 大写→小写(y/ 是 tr 操作符)

# ===== for + mv 基础操作 =====
for f in *; do mv "$f" "backup_$f"; done
i=1; for f in *.jpg; do mv "$f" "$(printf "photo_%03d.jpg" $i)"; ((i++)); done
for f in *\ *; do mv "$f" "${f// /_}"; done   # bash 内建字符串替换
1
2
3
4
5
6
7
8
9
10
11
#工具#文件
上次更新: 2026/06/17, 12:47:39
文档转换
分割合并

← 文档转换 分割合并→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式