$value) { $path = realpath($dir . DIRECTORY_SEPARATOR . $value); if (!is_dir($path)) { $results[] = $path; } else if ($value != "." && $value != "..") { GET_DIR_CONTENTS($path, $results); $results[] = $path; } } return $results; } function ECHO_USAGE(){ global $argv; echo "\n > Usage:\n"; echo " \"php " . $argv[0] . " \"\n"; echo " where is the directory to traverse and\n"; echo " is the regular expression to\n"; echo " search for, and is the raw string\n"; echo " to replace the search-regex with. Note that\n"; echo " is NOT a regular expression.\n"; echo " If contains one or more whitespace\n"; echo " character(s), it must be enclosed within double-\n"; echo " quotation marks.\n"; echo "\n > Examples:\n"; echo " > \"php " . $argv[0] . " . /.[a-z]+/ .jpg\"\n"; echo " > \"php " . $argv[0] . " . /.[a-zA-Z]+/ .jpg\"\n"; echo " > \"php " . $argv[0] . " . /.[a-zA-Z0-9]+/ \".jpg\"\"\n"; } // @BRIEF Replace $search with $replace in $str. This performs a simple replace using PHP's str_replace(). function REPLACE($str, $search, $replace){ return str_replace($search, $replace, $str); } // @BRIEF Replace $search with $replace in $str. This expects a regular expression for $search and uses PHP's preg_replace(). function REPLACE2($str, $search, $replace){ return preg_replace($search, $replace, $str); } // Returns true if both strings are the same. Performs a case-insensitive comparison unless third parameter is true. function ARE_STRINGS_EQUAL($str1, $str2, $case_sensitive = false){ switch($case_sensitive){ case true: if (strcmp($str1, $str2) == 0){ return true; } else{ return false; } break; default: if (strcasecmp($str1, $str2) == 0){ return true; } else{ return false; } break; } return false; } // ====================================================================== if (isset($argv[1])){ $dirToTraverse = $argv[1]; echo " > Check passed; Traversal Directory = [" . $dirToTraverse . "]\n"; } else{ echo " > Check FAILED; Missing Traversal Directory!\n"; ECHO_USAGE(); echo "\n > Script terminated.\n"; exit(1); } if (isset($argv[2])){ $regex_search = $argv[2]; echo " > Check passed; Search RegEx = \"" . $regex_search . "\"\n"; } else{ echo " > Check FAILED; Missing Search RegEx!\n"; ECHO_USAGE(); echo "\n > Script terminated.\n"; exit(1); } if (isset($argv[3])){ $replace_str = $argv[3]; echo " > Check passed; Replacement String = \"" . $replace_str . "\"\n"; } else{ echo " > Check FAILED; Missing Search RegEx!\n"; ECHO_USAGE(); echo "\n > Script terminated.\n"; exit(1); } echo " > Performing initial directory traversal, please wait...\n"; $script_time_start = microtime(true); $contents = array(); GET_DIR_CONTENTS($dirToTraverse, $contents); if ($remove_self){ while( ($found = array_search($thisFile, $contents)) !== false ){ echo " > Removing this script from file listing.\n"; unset($contents[$found]); } } // var_dump($contents); $script_time_end = microtime(true); $script_runtime = $script_time_end - $script_time_start; echo " > Initial directory traversal complete [" . $script_runtime . " ms].\n"; $counter = 0; $stop_preview = false; if ($enable_dir_scan_preview){ echo " > ORIGINAL FILE AND DIRECTORY PREVIEW:\n ================================\n"; foreach ($contents as $c){ if ($stop_preview) break; $counter++; if ($counter > $max_preview_size){ echo " --------- (results truncated)\n"; $stop_preview = true; break; } echo " " . $c . "\n"; } echo " ================================\n"; } $content_map = array(); $content_map_new = array(); // V2 Update: Build a content map with ONLY the path, while // honoring the $skip_directory setting. The content map will // be roughly the same if $skip_directories is false but should // usually be shortened if the aforementioned setting is true. foreach ($contents as $c){ switch($skip_directories){ case true: if (!is_dir($c)) array_push($content_map, $c); break; default: array_push($content_map, $c); break; } } // Instead of copying the entire array, we'll add to it as necessary later. // $content_map_new = $content_map; echo " > Creating new content map based on supplied search and replace settings, please wait...\n"; // print_r($content_map); // Build the replacement content map. This contains the same path => filename // format but with the updated filename instead of the original. $script_time_start = microtime(true); // foreach ($content_map_new as $p => $f){ // V2 Update: Build a new content map, with ONLY values that will be replaced, // in key => value pairs as old_value => new_value. foreach ($content_map as $p){ // Keep track of old and new filenames. $filename = $p; $filename_new = $filename; // Perform the search & replacement. $filename_new = REPLACE2($filename_new, $regex_search, $replace_str); // If original and new filenames are different, then a replacement was made. // Add this to the new content map. if (!ARE_STRINGS_EQUAL($filename, $filename_new)){ $content_map_new[$filename] = $filename_new; } } // Record number of items for use later. $total_items = count($content_map_new); // Account for zero-based indexing. if ($total_items > 0) $total_items++; $script_time_end = microtime(true); $script_runtime = $script_time_end - $script_time_start; echo " > Success: new content map created [" . $script_runtime . " ms].\n"; echo " > FILE AND DIRECTORY PREVIEW:\n ================================\n"; $counter = 0; $stop_preview = false; // Display preview of new content map. foreach ($content_map_new as $old => $new){ if ($stop_preview) break; if (!is_dir($p)){ $counter++; if ($counter > $max_preview_size){ echo " --------- (results truncated)\n"; $stop_preview = true; break; } echo " [before]: " . $old . "\n"; echo " [after]: " . $new . "\n\n"; } } echo "\n >>> A total of " . $total_items . " item(s) will be renamed. <<<\n\n"; if ($total_items > 0){ echo " *** PLEASE REVIEW ABOVE PREVIEW RESULTS CAREFULLY BEFORE PROCEEDING. ***\n\n"; echo " - To begin operation (cannot be undone), type 'Y' and press ENTER/RETURN.\n"; // echo " - To preview more results, type 'P' and press ENTER/RETURN.\n"; echo " - To cancel script without making any changes, type 'N' and press ENTER/RETURN.\n"; echo "\n >> Begin operation? [Y/n]: "; $response = fgets(STDIN); $begin_operation = substr($response, 0, strlen($response) - 1); if (ARE_STRINGS_EQUAL($begin_operation, "Y", true)){ echo "\n > Operation started by user.\n"; $countdown_timer = 5; while($countdown_timer > 0){ echo " > Operation starting in " . $countdown_timer . " second(s)...\n"; $countdown_timer--; sleep(1); } echo "\n > Operation in progress, please wait...\n"; $items_remaining = $total_items; $curr_item = 0; $progress_val = 0; $script_time_start = microtime(true); foreach ($content_map_new as $old => $new){ $curr_item++; $progress_val = $curr_item / $total_items; $progress_val *= 100; $progress_val_d = number_format((float)$progress_val, 2, '.', ''); // TODO: Only perform this check if skip_directories is TRUE. if (!is_dir($p)){ // Actual command to be executed. $cmd = "mv \"" . $old . "\" \"" . $new . "\""; // Truncated command to be displayed. $cmd_d = "-"; // Make sure the strings are actually long enough to get truncated. If either are too short, // then just display the full command. if (strlen($old) >= -$cmd_display_limit && strlen($new) >= -$cmd_display_limit){ $cmd_d = "mv \"..." . substr($old, $cmd_display_limit) . "\" \"..." . substr($new, $cmd_display_limit) . "\""; } else{ $cmd_d = $cmd; } if (!$display_progress && !$display_full_cmd){ //00 // print nothing } else if (!$display_progress && $display_full_cmd){ //01 echo " >>> [" . $cmd_d . "]\n"; } else if ($display_progress && !$display_full_cmd){ //10 echo " >>> [Progress: " . $progress_val_d . "% (" . $curr_item . "/" . $total_items . ") | Items Remaining: " . $items_remaining . "]\n"; } else if ($display_progress && $display_full_cmd){ //11 echo " >>> [Progress: " . $progress_val_d . "% (" . $curr_item . "/" . $total_items . ") | Remaining: " . $items_remaining . "] | [" . $cmd_d . "]\n"; } exec($cmd); } $items_remaining--; } $script_time_end = microtime(true); $script_runtime = $script_time_end - $script_time_start; echo " > Operation complete. [" . $script_runtime . " ms].\n"; exit(0); } echo "\n > Operation cancelled by user.\n"; exit(0); } else{ echo "\n > Operation cancelled: there are no items to replace.\n"; exit(0); }