Skip to content

Instantly share code, notes, and snippets.

@Bill-Stewart
Created June 11, 2024 20:08
Show Gist options
  • Save Bill-Stewart/cbe4596e00e49b62ee7a34616d237599 to your computer and use it in GitHub Desktop.
Save Bill-Stewart/cbe4596e00e49b62ee7a34616d237599 to your computer and use it in GitHub Desktop.
// Renamer.js
// Written by Bill Stewart (bstewart AT iname.com)
//
// Renames files and/or directories using regular expressions.
var SCRIPT_NAME = "Renamer.js",
ERROR_INVALID_PARAMETER = 87;
var FSO;
WScript.Quit(main());
// Displays a usage message and exits the script.
function usage() {
WScript.Echo("Renames files and/or directories using regular expressions.\n"
+ "\n"
+ "Usage:\t" + SCRIPT_NAME + " <directory> [...] [/f:<str>] [/r:<str>] [/o:<d|f|df>] [/c]\n"
+ "\t[/p] [/s] [/t]\n"
+ "\n"
+ "<directory> - Specify one or more directory names on the command line\n"
+ "/f:<str> - Regular expression to find in file/directory names (default=space)\n"
+ "/r:<str> - Replacement string (default=_)\n"
+ "/o:<d|f|df> - What to rename? d=directories, f=files, df=both (default=f)\n"
+ "/c - Case-sensitive pattern matching (don't ignore case)\n"
+ "/p - Prompt to rename each file/directory\n"
+ "/s - Recurse through subdirectories\n"
+ "/t - Test mode: Only display what will be renamed\n"
+ "\n"
+ "USE THIS SCRIPT WITH EXTREME CAUTION. It can potentially render a system\n"
+ "unbootable by renaming system files/directories, and it can break applications\n"
+ "by renaming critical files/directories.\n"
+ "\n"
+ "The script does not test whether a directory or a file's new name is valid\n"
+ "before attempting to rename it, so test mode (/t) may report inaccurate results\n"
+ "if a new name is not valid.");
WScript.Quit(0);
}
// Returns the script host that executing this script, in lowercase
// (cscript.exe or wscript.exe).
function scriptHost() {
return WScript.FullName.substring(WScript.Path.length + 1).toLowerCase();
}
// Workalike to VBScript's Hex function.
function hex(n) {
if ( n < 0 ) {
return (n + 0x100000000).toString(0x10).toUpperCase();
}
else {
return n.toString(0x10).toUpperCase();
}
}
// Displays a prompt and waits for user input. Returns true if the
// user types a string that begins with the letter "y", or false
// otherwise.
function query(prompt) {
WScript.StdOut.Write(prompt);
var response = WScript.StdIn.ReadLine().toLowerCase().charAt(0);
return response == "y";
}
// Renames the specified File or Folder object (o) to a new name.
// Returns true if operation succeeded, or false if it failed. Note:
// Returns true if options.testMode is true. Errors get output to
// stderr.
function rename(o,newName,options) {
var oldName = FSO.BuildPath(FSO.GetParentFolderName(o),o.Name);
try {
if ( ! options.testMode ) {
if ( options.prompt ) {
if ( ! query(oldName + " -> " + newName + " ? ") ) {
return false;
}
}
o.Name = newName;
}
if ( ! options.prompt ) {
WScript.Echo(oldName + " -> " + newName);
}
return true;
}
catch(err) {
WScript.StdErr.WriteLine("Error 0x" + hex(err.number) + " renaming " + oldName + " to " + newName);
options.errorCount++;
return false;
}
}
// Processes the specified Folder object based on the parameters
// passed in the options object.
function process(folder,options) {
// Process directory names if requested.
if ( options.dirs ) {
var folderColl = new Enumerator(folder.SubFolders);
for ( ; ! folderColl.atEnd(); folderColl.moveNext() ) {
var subFolder = folderColl.item();
if ( subFolder.Name.search(options.re) != -1 ) {
var newName = subFolder.Name.replace(options.re,options.newStr);
if ( rename(subFolder,newName,options) ) {
options.dirCount++;
}
}
}
}
// Recurse if requested.
if ( options.recurse ) {
var folderColl = new Enumerator(folder.SubFolders);
for ( ; ! folderColl.atEnd(); folderColl.moveNext() ) {
var subFolder = folderColl.item();
process(subFolder,options);
}
}
// Process file names if requested.
if ( options.files ) {
var fileColl = new Enumerator(folder.Files);
for ( ; ! fileColl.atEnd(); fileColl.moveNext() ) {
var file = fileColl.item();
if ( file.Name.search(options.re) != -1 ) {
var newName = file.Name.replace(options.re,options.newStr);
if ( rename(file,newName,options) ) {
options.fileCount++;
}
}
}
}
}
function main() {
var args = WScript.Arguments;
if ( (args.Unnamed.length == 0) || (args.Named.Exists("?")) ) {
usage();
}
if ( scriptHost() != "cscript.exe" ) {
WScript.Echo("You must run this script using the CScript host");
return 1;
}
// Create an options object to pass to the process function.
var options = {
oldStr: "", // String to search for
newStr: "", // String to replace it with
re: null, // Search regexp
recurse: false, // Recurse subdirectories?
testMode: false, // Test mode?
dirs: false, // Rename directories?
files: false, // Rename files?
prompt: false, // Prompt before each rename?
dirCount: 0, // Number of directories renamed
fileCount: 0, // Number of files renamed
errorCount: 0 // Number of errors
};
// If /f specifies a string to find, use it;
// otherwise, default to a space.
if ( args.Named("f") != undefined ) {
options.oldStr = args.Named.Item("f");
if ( options.oldStr == "" ) {
WScript.Echo("Find expression not specified");
return ERROR_INVALID_PARAMETER;
}
}
else {
options.oldStr = " ";
}
// If /r specifies a replacement string, use it;
// otherwise, default to an underscore.
if ( args.Named("r") != undefined ) {
options.newStr = args.Named.Item("r");
}
else {
options.newStr = "_";
}
if ( options.oldStr == options.newStr ) {
WScript.Echo("/f and /r must specify different strings");
return ERROR_INVALID_PARAMETER;
}
// Test for the presence of /p, /s, and /t.
options.prompt = args.Named.Exists("p");
options.recurse = args.Named.Exists("s");
options.testMode = args.Named.Exists("t");
// /t takes precendence over /p.
if ( options.testMode ) {
options.prompt = false;
}
// If /o specifies something, see if d and/or f are specified as
// arguments. Otherwise, assume /o:f.
if ( args.Named.Item("o") != undefined ) {
options.dirs = args.Named.Item("o").toLowerCase().indexOf("d") != -1;
options.files = args.Named.Item("o").toLowerCase().indexOf("f") != -1;
}
else {
options.files = true;
}
// Fail if d and/or if not specified after /o.
if ( (! options.dirs) && (! options.files) ) {
WScript.Echo("You must specify /o:d, /o:f, or /o:df");
return ERROR_INVALID_PARAMETER;
}
// Create the RegExp containing the find expression. If /c exists
// on the command line, don't ignore case.
if ( args.Named.Exists("c") ) {
options.re = new RegExp(options.oldStr,"g");
}
else {
options.re = new RegExp(options.oldStr,"gi");
}
FSO = new ActiveXObject("Scripting.FileSystemObject");
// Iterate the collection of directories named on the command line.
var dirColl = new Enumerator(args.Unnamed);
for ( ; ! dirColl.atEnd(); dirColl.moveNext() ) {
var dir = dirColl.item();
if ( ! FSO.FolderExists(dir) ) {
WScript.Echo("Directory not found - " + dir);
}
else {
process(FSO.GetFolder(dir),options);
}
}
var output = options.testMode ? "\nTest results:" : "\nResults:";
if ( options.dirs ) {
if ( options.dirCount == 1 ) {
output += "\n1 directory renamed";
}
else {
output += "\n" + options.dirCount.toString() + " directories renamed";
}
}
if ( options.files ) {
output += "\n" + options.fileCount.toString() + " file(s) renamed";
}
if ( options.errorCount > 0 ) {
output += "\n" + options.errorCount.toString() + " error(s)";
}
WScript.Echo(output);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment