批量重命名是維運、資料整理常見需求。本文提供一個健壯的 PHP 腳本:遞歸掃描目錄、支援正則/字串替換、乾跑模式預覽、防覆蓋保護、詳細日誌,可直接用於生產環境(建議先備份)。
glob() + 遞歸,支援深層目錄。[1] 字樣,可自訂正則/回呼。$dryRun = true 僅輸出預覽,不實際改名。<?php
/**
* 批量重命名檔案
* @param string $rootDir 根目錄
* @param callable $renameFn 檔名轉換函數,接收舊檔名返回新檔名(不含路徑)
* @param bool $dryRun true=預覽模式,false=實際執行
* @return array 統計結果
*/
function batchRename(string $rootDir, callable $renameFn, bool $dryRun = true): array {
$stats = ['renamed' => 0, 'skipped' => 0, 'errors' => 0, 'details' => []];
// 遞歸收集所有檔案
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
if ($file->isFile()) $files[] = $file->getPathname();
}
foreach ($files as $oldPath) {
$dir = dirname($oldPath);
$oldName = basename($oldPath);
$newName = $renameFn($oldName);
if ($newName === $oldName) {
$stats['skipped']++;
$stats['details'][] = ['old' => $oldPath, 'new' => '', 'status' => 'unchanged'];
continue;
}
$newPath = $dir . DIRECTORY_SEPARATOR . $newName;
// 防覆蓋檢查
if (file_exists($newPath)) {
$stats['errors']++;
$stats['details'][] = ['old' => $oldPath, 'new' => $newPath, 'status' => 'target_exists'];
continue;
}
if (!$dryRun) {
if (@rename($oldPath, $newPath)) {
$stats['renamed']++;
$stats['details'][] = ['old' => $oldPath, 'new' => $newPath, 'status' => 'ok'];
} else {
$stats['errors']++;
$stats['details'][] = ['old' => $oldPath, 'new' => $newPath, 'status' => 'rename_failed', 'error' => error_get_last()['message'] ?? 'unknown'];
}
} else {
$stats['renamed']++; // 預覽模式計入重命名數
$stats['details'][] = ['old' => $oldPath, 'new' => $newPath, 'status' => 'dry_run'];
}
}
return $stats;
}
// === 使用範例 ===
$root = __DIR__ . '/uploads'; // 目標目錄
// 規則:移除檔名中的 [1]、[2] 等編號標記
$rule = function(string $name): string {
return preg_replace('/\[\d+\]/', '', $name);
};
// 1. 先乾跑預覽
$result = batchRename($root, $rule, true);
echo "=== 預覽模式 ===\n";
echo "將重命名: {$result['renamed']} 個\n";
echo "跳過(同名): {$result['skipped']} 個\n";
foreach ($result['details'] as $d) {
if ($d['status'] === 'dry_run') {
echo " {$d['old']} → {$d['new']}\n";
}
}
// 2. 確認無誤後執行
// $result = batchRename($root, $rule, false);
// echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
?>
| 需求 | 回呼函數 |
|---|---|
| 移除 [數字] | fn($n) => preg_replace('/\[\d+\]/', '', $n) |
| 統一小寫 | fn($n) => strtolower($n) |
| 空格改底線 | fn($n) => str_replace(' ', '_', $n) |
| 加前綴日期 | fn($n) => date('Ymd_') . $n |
| 副檔名統一 .jpg | fn($n) => preg_replace('/\.[^.]+$/', '.jpg', $n) |
| 複雜正則捕獲重組 | fn($n) => preg_replace('/(IMG_)(\d+)/', '$1$2_edited', $n) |
更多 PHP 檔案系統操作、SPL 迭代器進階、inotify 監控檔案變更、大量檔案處理效能優化、跨平台路徑處理最佳實踐,請參考本站專題系列。