Shell 脚本批量重命名包含特殊字符的文件,最稳妥的方式是使用 mv 命令配合双引号包裹变量,或使用 rename 工具进行正则替换。适用场景为 Linux 或 macOS 终端环境,主要风险边界在于文件名包含换行符或脚本未做存在性检查导致覆盖数据。
先说结论:优先使用 mv 命令配合变量 quoting 处理特殊字符,复杂正则替换建议使用 rename。
- 适合:Linux/macOS 终端环境,文件数量在脚本可遍历范围内
- 先看:文件名是否包含空格、引号、星号或换行符
- 建议:执行重命名前先用 echo 命令模拟输出确认路径
命令速用版
# 将当前目录下所有空格替换为下划线
for file in *; do
[ -e "$file" ] || continue
newname=$(echo "$file" | tr ' ' '_')
[ "$file" != "$newname" ] && mv -n -- "$file" "$newname"
done为什么会这样
Shell 会将空格、星号等解析为参数分隔符或通配符。未经引号包裹的变量在展开时会被 Shell 重新分割,导致 mv 命令接收到错误的参数数量或匹配到意外文件。
分步处理
第一步列出所有目标文件,使用 find 或 ls 确认特殊字符位置。
第二步构造重命名逻辑,使用 ${var//pattern/replace} 进行字符串替换。
第三步执行 dry-run,将 mv 替换为 echo 检查生成的命令是否符合预期。
第四步移除 echo 执行实际重命名,添加 -n 参数防止覆盖现有文件。
怎么验证是否生效
使用 ls -l 命令查看文件列表,确认旧文件名消失且新文件名存在。
使用 stat 命令检查文件 inode 节点是否未变,确保是重命名而非复制。
常见坑
不同系统的 rename 命令实现不同,Perl 版本支持正则,util-linux 版本支持简单替换。
文件名包含换行符时 for 循环会失效,需改用 find -print0 配合 while read 处理。
未加 -- 参数时,以减号开头的文件名会被 mv 解析为选项导致报错。
常见问题
文件名中有空格必须转义吗?
必须使用双引号包裹变量。在 Shell 脚本中引用变量时写成 "$file" 即可自动处理空格,无需手动添加反斜杠。
rename 命令为什么报错?
不同发行版预装的 rename 工具不同。Debian/Ubuntu 默认是 Perl 版本,CentOS 可能是 util-linux 版本,语法不兼容。
如何安全回滚重命名操作?
脚本执行前备份文件列表。若需回滚,需编写反向映射脚本将新名称改回旧名称,mv 命令本身不支持 undo。
参考来源
- GNU Bash Manual, Quoting, https://www.gnu.org/software/bash/manual/
- GNU Coreutils, mv invocation, https://www.gnu.org/software/coreutils/manual/html_node/mv-invocation.html