Listing 1: Renamer.js // Renamer.js // Written by Bill Stewart (bill.stewart@frenchmortuary.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 + " [...] [/f:] [/r:] [/o:] [/c]\n" + "\t[/p] [/s] [/t]\n" + "\n" + " - Specify one or more directory names on the command line\n" + "/f: - Regular expression to find in file/directory names (default=space)\n" + "/r: - Replacement string (default=_)\n" + "/o: - 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.substr(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) { var response; WScript.StdOut.Write(prompt); 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; 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.errors++; return false; } } // Processes the specified Folder object based on the parameters // passed in the options object. // ******** BEGIN CALLOUT A ******** function process(folder, options) { var foldercoll, subfolder, filecoll, file, newname; // Process directory names if requested. if (options.dirs) { foldercoll = new Enumerator(folder.SubFolders); for (; ! foldercoll.atEnd(); foldercoll.moveNext()) { subfolder = foldercoll.item(); if (subfolder.Name.search(options.re) != -1) { newname = subfolder.Name.replace(options.re, options.newstr); if (rename(subfolder, newname, options)) options.dirtotal++; } } } // Recurse if requested. if (options.recurse) { foldercoll = new Enumerator(folder.SubFolders); for (; ! foldercoll.atEnd(); foldercoll.moveNext()) { subfolder = foldercoll.item(); process(subfolder, options); } } // Process file names if requested. if (options.files) { filecoll = new Enumerator(folder.Files); for (; ! filecoll.atEnd(); filecoll.moveNext()) { file = filecoll.item(); if (file.Name.search(options.re) != -1) { newname = file.Name.replace(options.re, options.newstr); if (rename(file, newname, options)) options.filetotal++; } } } } // ******** END CALLOUT A ******** function main() { var args, options, dircoll, dir, output; 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; } // ******** BEGIN CALLOUT B ******** // Create an options object to pass to the process function. options = { oldstr: "", // String to search for newstr: "", // String to replace it with re: new RegExp(), // Search regexp recurse: false, // Recurse subdirectories? testmode: false, // Test mode? dirs: false, // Rename directories? files: false, // Rename files? prompt: false, // Prompt before each rename? dirtotal: 0, // Number of directories renamed filetotal: 0, // Number of files renamed errors: 0 // Number of errors }; // ******** END CALLOUT B ******** // 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; } // ******** BEGIN CALLOUT C ******** // 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"); // ******** END CALLOUT C ******** // /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; } // ******** BEGIN CALLOUT D ******** // Compile the regexp containing the find expression. If /c exists // on the command line, don't ignore case. if (args.Named.Exists("c")) options.re.compile(options.oldstr, "g"); else options.re.compile(options.oldstr, "gi"); // ******** END CALLOUT D ******** FSO = new ActiveXObject("Scripting.FileSystemObject"); // Iterate the collection of directories named on the command line. dircoll = new Enumerator(args.Unnamed); for (; ! dircoll.atEnd(); dircoll.moveNext()) { dir = dircoll.item(); if (! FSO.FolderExists(dir)) WScript.Echo("Directory not found - " + dir); else { process(FSO.GetFolder(dir), options); } } output = options.testmode ? "\nTest results:" : "\nResults:"; if (options.dirs) { if (options.dirtotal == 1) output += "\n1 directory renamed"; else output += "\n" + options.dirtotal.toString() + " directories renamed"; } if (options.files) output += "\n" + options.filetotal.toString() + " file(s) renamed"; if (options.errors > 0) output += "\n" + options.errors.toString() + " error(s)"; WScript.Echo(output); return 0; } 5