Skip to content

Instantly share code, notes, and snippets.

@daemondevin
Created March 7, 2026 10:06
Show Gist options
  • Select an option

  • Save daemondevin/dc0868321e325162ac4c120a7f752637 to your computer and use it in GitHub Desktop.

Select an option

Save daemondevin/dc0868321e325162ac4c120a7f752637 to your computer and use it in GitHub Desktop.
PHP CLI filename renamer with wildcard capture
<?php
/**
* CLI filename renamer with wildcard capture
*
* Example:
* php replaceFilename.php --find=frontendadmin.*.php --replace=ts*.snippet.php --regex=*
*/
$options = getopt("", [
"find:",
"replace:",
"regex:",
"dir::",
"dry-run::",
"recursive::"
]);
if (!isset($options['find'], $options['replace'], $options['regex'])) {
echo "Usage:\n";
echo "php replaceFilename.php --find=PATTERN --replace=PATTERN --regex=CHAR [--dir=PATH] [--dry-run] [--recursive]\n";
exit(1);
}
$findPattern = $options['find'];
$replacePattern = $options['replace'];
$regexToken = $options['regex'];
$directory = $options['dir'] ?? getcwd();
$dryRun = array_key_exists('dry-run', $options);
$recursive = array_key_exists('recursive', $options);
if (!is_dir($directory)) {
echo "Invalid directory: $directory\n";
exit(1);
}
/**
* Convert the find pattern to regex
*/
function buildRegex($pattern, $token) {
$escaped = preg_quote($pattern, '/');
$escaped = str_replace(preg_quote($token, '/'), '(.+)', $escaped);
return '/^' . $escaped . '$/';
}
/**
* Build replacement filename
*/
function buildReplacement($pattern, $capture, $token) {
return str_replace($token, $capture, $pattern);
}
$regex = buildRegex($findPattern, $regexToken);
$total = 0;
$renamed = 0;
$skipped = 0;
$iterator = $recursive
? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory))
: new IteratorIterator(new DirectoryIterator($directory));
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
$filename = $file->getFilename();
if (preg_match($regex, $filename, $matches)) {
$total++;
$capture = $matches[1];
$newName = buildReplacement($replacePattern, $capture, $regexToken);
$oldPath = $file->getPathname();
$newPath = $file->getPath() . DIRECTORY_SEPARATOR . $newName;
if (file_exists($newPath)) {
echo "SKIP (exists): $filename -> $newName\n";
$skipped++;
continue;
}
if ($dryRun) {
echo "DRY RUN: $filename -> $newName\n";
$renamed++;
} else {
if (rename($oldPath, $newPath)) {
echo "RENAMED: $filename -> $newName\n";
$renamed++;
} else {
echo "FAILED: $filename\n";
$skipped++;
}
}
}
}
echo "\n------ Summary ------\n";
echo "Matched : $total\n";
echo "Renamed : $renamed\n";
echo "Skipped : $skipped\n";
echo "Directory: $directory\n";
echo "Mode: " . ($dryRun ? "DRY RUN" : "LIVE") . "\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment