批量重命名需确保路径绝对化、目标目录存在且可写、中文编码正确;跨磁盘需用copy+unlink;保留时间戳须stat+touch或touch -r。
rename() 批量改文件名要小心路径和编码
直接用 rename() 改名本身没问题,但批量操作时容易出错——尤其是中文文件名、相对路径没写全、目标目录不存在,或者 Windows 和 Linux 下的大小写敏感差异。别指望一次循环就跑通,得先确保路径绝对化、目标目录可写、文件名不冲突。
rename() 第一个参数是旧路径,第二个是新路径,二者都必须是完整路径(建议用 realpath() 或 __DIR__ . '/xxx' 拼)scandir() 可能返回乱码,此时应避免直接用文件名拼接,改用 mb_convert_encoding() 转码mkdir() 目标父目录,否则 rename() 会失败并返回 false
,且不报错glob() + foreach 实现安全批量重命名比 scandir() 更可控:能按扩展名过滤,天然跳过 . 和 ..,且返回的是带路径的完整字符串,减少拼接错误。
foreach (glob(__DIR__ . '/old/*.jpg') as $old) {
$new = str_replace('old/', 'new/', $old); // 简单前缀替换
if (!is_dir(dirname($new))) {
mkdir(dirname($new), 0755, true);
}
if (rename($old, $new)) {
echo "OK: " . basename($old) . " → " . basename($new) . "\n";
} else {
echo "FAIL: " . basename($old) . "\n";
}
}
/old/xxx.jpg 改成 /new/xxx.jpg,注意 str_replace() 是字符串替换,不是正则;如需更灵活匹配,请用 preg_replace()
rename() 后检查返回值,不要只靠 echo 或静默跳过/home 移到 /mnt/usb),rename() 会失败,此时得用 copy() + unlink()
find + mv 更快更稳PHP 脚本适合小规模或需业务逻辑介入的场景;纯文件名替换,Shell 效率高、原子性强,而且绕开了 PHP 的编码和权限陷阱。
find ./photos -name "IMG_*.jpg" -exec bash -c 'mv "$1" "${1/IMG_/NEW_}"' _ {} \;
photos/ 中所有 IMG_xxx.jpg 改成 NEW_xxx.jpg,${1/IMG_/NEW_} 是 Bash 参数扩展,比 sed 更轻量echo 预览:把 mv 换成 echo mv,确认输出无误再删掉 echo
rename() 和 mv 都会更新文件的 mtime 和 ctime,如果你依赖文件修改时间做同步或备份判断,这会导致误判。PHP 里没法用 rename() 保留时间戳;必须用 touch() 手动恢复:
$stat = stat($old); rename($old, $new); touch($new, $stat['mtime'], $stat['atime']);
stat() 返回数组里 mtime 是修改时间,atime 是访问时间,ctime(inode change time)无法通过 touch() 设置,PHP 没有对应函数touch -r oldfile newfile 命令复制时间戳,比 PHP 更准rsync -t 或基于 mtime 的增量备份,就会发现同步异常