批量改名的通用公式
Linux 上没有统一的批量改名命令,通用做法是「通配符取文件 → for 循环 → mv → 参数扩展拼新名字」,或者用 rename 这类专用工具。这类操作的风险集中在三处:文件名里的空格与特殊字符、目标名与已有文件撞名、以及干跑与执行不是同一条命令导致结果和预期不一致。
基本信息
| 适用平台 | Linux/Unix(bash 或兼容 shell) |
|---|---|
| 通用写法 | for f in 通配符; do mv 原 新; done |
| 参数扩展 | ${f%.jpeg} 去掉后缀,${f##*/} 取文件名,${f%%.*} 去掉最长的后缀 |
| 专用工具 | rename,注意存在 Perl 版与 util-linux 版两种不兼容实现 |
| 纪律 | 先 echo 干跑打印将要执行的动作,确认无误后再把 echo 去掉 |
典型使用场景
- 统一扩展名:把 *.jpeg 全改成 *.jpg
- 批量加前缀或后缀:给导出的报表统一加日期
- 去掉下载文件名里的多余后缀(.txt.1 这类重复后缀)
- 按规则编号:给一批图片加上序号前缀
- 递归改名:目录树里所有匹配文件统一调整,配合 find 处理
循环版:先干跑再执行
循环版:先干跑再执行
for f in *.jpeg; do echo mv -- "$f" "${f%.jpeg}.jpg"; done # 干跑:只打印
for f in *.jpeg; do mv -- "$f" "${f%.jpeg}.jpg"; done # 确认后再执行
for f in *.txt; do mv -- "$f" "archived-$f"; done # 加前缀
for f in *; do echo "$f" "${f%%.*}"; done # 先看参数扩展结果rename 版:先分清实现
rename 版:先分清实现
rename 's/[.]jpeg$/.jpg/' *.jpeg # Perl 版:sed 风格表达式
rename -n 's/[.]jpeg$/.jpg/' *.jpeg # Perl 版的干跑
rename .jpeg .jpg *.jpeg # util-linux 版:纯字符串替换
rename --version # 先确认装的是哪一个递归与特殊文件名
递归与特殊文件名
find . -type f -name '*.jpeg' -print0 | while IFS= read -r -d '' f; do
echo mv -- "$f" "${f%.jpeg}.jpg" # 用 -print0 与 -d '' 兼容空格与换行
done改名写法对比
| 写法 | 适用场景 | 风险点 |
|---|---|---|
| for + mv | 当前目录、文件名规律 | 变量不加引号会被拆词 |
| 参数扩展 | 拼接后缀与前缀 | 写成 ${f%%...} 会多吃一段 |
| Perl 版 rename | 正则替换、条件改名 | 语法与 util-linux 版完全不同 |
| util-linux 版 rename | 简单字符串替换 | 只做字面替换,没有正则能力 |
| find -print0 | 递归、文件名含空格 | 必须配 read -d '' 或 xargs -0 |
参数扩展是这条公式的核心:${f%.jpeg} 从右边去掉最短匹配的 .jpeg,${f%%.jpeg} 去掉最长匹配,${f##*/} 取出文件名部分。先单独跑一遍 echo 看扩展结果,比在 mv 里试错安全得多。
rename 有两个同名不同实现的版本:Perl 版接受 sed 风格表达式(s/a/b/),util-linux 版只做字面替换。在别人的机器上直接抄命令很容易报错或改错名字,先跑 rename --version 确认,再用干跑选项预览。
给一批文件加序号时用 printf 做零填充,能让 ls 与排序保持自然顺序,避免 10 排在 2 前面:先 for f in *.jpg 循环取文件,计数器初始化为 i=1 并逐次递增,目标名写成 $(printf '%03d' $i)-$f。加工号或日期前缀时同理,长度固定才能排序正确。