Script to rename files

11,726

Solution 1

I need about 2 minutes to write such script for *NIX systems (may be less), but for Windows it is a long song ... ))

I've write simple VBS script for WSH, try it (save to {script-name}.vbs, change Path value (on the first line of the script) and execute). I recommend to test script on small amount of data for the first time just to be sure if it works correctly.

Path = "C:\Users\rootDirectory"
Set FSO = CreateObject("Scripting.FileSystemObject")

Sub visitFolder(folderVar)
    For Each fileToRename In folderVar.Files
        fileToRename.Name = "Agreement " & fileToRename.Name
    Next
    For Each folderToVisit In folderVar.SubFolders
        visitFolder(folderToVisit)
    Next
End Sub

If FSO.FolderExists(Path) Then
    visitFolder(FSO.getFolder(Path))
End If

Solution 2

I used to do bulk renaming with batch scripts under Windows. I know it's a snap on *nix (find . -maxdepth N -type f -name "$pattern" | sed -e 'p' -e "s/$str1/$str2/g" | xargs -n2 mv). Buf after some struggle in vain, I found out, to achieve that effect using batch scripts is almost impossible. So I turned to javascript.

With this script, you can add prefix to file names by 'rename.js "s/^/Agreement /" -r *.doc'. A caret(^) means to match the beginning. The '-r' options means 'recursively', i.e. including sub-folders. You can specify a max depth with the '-d N' option. If neither '-r' or '-d N' is given, the script does not recurse.

If you know the *nix 'find' utility, you would notice that 'find' will match the full path (not just the file name part) to specified regular expression. This behavior can be achieved by supplying the '-f' option. By default, this script will match the file name part with the given regular expression.

If you are familiar with regular expressions, complicated renaming is possible. For example, 'rename.js "s/(\d+)/[$1]/" *' which uses grouping to add brackets to number sequences in filenames.

// rename.js --- bulk file renaming utility (like *nix rename.pl)
// (c) Copyright 2012, Ji Han (hanji <at> outlook <dot> com)
// you are free to distribute it under the BSD license.

// oops... jscript doesn't have array.map
Array.prototype.map = function(f, t){
    var o = Object(this);
    var a = new Array(o.length >>> 0);
    for (var i = 0; i < a.length; ++i){ if (i in o) a[i] = f.call(t, o[i], i, o) }
    return a;
};

/// main
(function(){

if (WScript.Arguments.Length == 0){
    WScript.Echo('rename "<operator>/<pattern>/<string>/[<modifiers>]" [-f] [-r] [-d <maxdepth>] [<files>]');
    WScript.Quit(1);
}

var fso = new ActiveXObject('Scripting.FileSystemObject');

// folder is a Folder object [e.g. from fso.GetFolder()]
// fn is a function which operates on File/Folder object
var recurseFolder = function(folder, fn, depth, maxdepth){
    if (folder.Files){
        for (var e = new Enumerator(folder.Files); !e.atEnd(); e.moveNext()){
            fn(e.item())
        }
    }
    if (folder.Subfolders){
        for (var e = new Enumerator(folder.SubFolders); !e.atEnd(); e.moveNext()){
            fn(e.item());
            if (depth < maxdepth){ arguments.callee(e.item(), fn, depth + 1, maxdepth) }
        }
    }
}

// expand wildcards (asterisk [*] and question mark [?]) recursively
// given path may be relative, and may contain environment variables.
// but wildcards only work for the filename part of a path.
// return an array of full paths of matched files.
// {{{
var expandWildcardsRecursively = function(n, md){
    var pattern = fso.GetFileName(n);
    // escape regex metacharacters (except  \, /, * and ?)
    // \ and / wouldn't appear in filename
    // * and ? are treated as wildcards
    pattern = pattern.replace(/([\[\](){}^$.+|-])/g, '\\$1');
    pattern = pattern.replace(/\*/g, '.*');  // * matches zero or more characters
    pattern = pattern.replace(/\?/g, '.');  // ? matches one character
    pattern = pattern.replace(/^(.*)$/, '\^$1\$');  // matches the whole filename
    var re = new RegExp(pattern, 'i');  // case insensitive
    var folder = fso.GetFolder(fso.GetParentFolderName(fso.GetAbsolutePathName(n)));
    var l = [];
    recurseFolder(folder, function(i){ if (i.Name.match(re)) l.push(i.Path) }, 0, md);
    return l;
}
// }}}

// parse "<operator>/<pattern>/<string>/[<modifiers>]"
// return an array splitted at unescaped forward slashes
// {{{
var parseExpr = function(s){
    // javascript regex doesn't have lookbehind...
    // reverse the string and lookahead to parse unescaped forward slashes.
    var z = s.split('').reverse().join('');

    // match unescaped forward slashes and get their positions.
    var re = /\/(\\\\)*(?!\\)/g;
    var l = [];
    while (m = re.exec(z)){ l.push(m.index) }

    // split s at unescaped forward slashes.
    var b = [0].concat(l.map(function(x){ return s.length - x }).reverse());
    var e = (l.map(function(x){ return s.length - x - 1 }).reverse()).concat([s.length]);
    return b.map(function(_, i){ return s.substring(b[i], e[i]) });
}
// }}}

var expr = WScript.Arguments(0);
var args = [];
var options = {};

for (var i = 1; i < WScript.Arguments.Length; ++i){
    if (WScript.Arguments(i).substring(0, 1) != '-'){
        args.push(WScript.Arguments(i));
    } else if (WScript.Arguments(i) == '-f'){
        options['fullpath'] = true;
    } else if (WScript.Arguments(i) == '-r'){
        options['recursive'] = true;
    } else if (WScript.Arguments(i) == '-d'){
        options['maxdepth'] = WScript.Arguments(++i);
    } else if (WScript.Arguments(i) == '--'){
        continue;
    } else {
        WScript.Echo('invalid option \'' + WScript.Arguments(i) +'\'');
        WScript.Quit(1);
    }
}

if (options['maxdepth']){
    var md = options['maxdepth'];
} else if (options['recursive']){
    var md = 1<<31>>>0;
} else {
    var md = 0;
}

var tokens = parseExpr(expr);
if (tokens.length != 4){
    WScript.Echo('error parsing expression \'' + expr + '\'.');
    WScript.Quit(1);
}
if (tokens[0] != 's'){
    WScript.Echo('<operator> must be s.');
    WScript.Quit(1);
}

var pattern = tokens[1];
var substr = tokens[2];
var modifiers = tokens[3];
var re = new RegExp(pattern, modifiers);

for (var i = 0; i < args.length; ++i){
    var l = expandWildcardsRecursively(args[i], md);
    for (var j = 0; j < l.length; ++j){
        var original = l[j];
        if (options['fullpath']){
            var nouveau = original.replace(re, substr);
        } else {
            var nouveau = fso.GetParentFolderName(original) + '\\' + fso.GetFileName(original).replace(re, substr);
        }
        if (nouveau != original){
            (fso.FileExists(original) && fso.GetFile(original) || fso.GetFolder(original)).Move(nouveau)
        }
    }
}

})();
Share:
11,726
user1096207
Author by

user1096207

Updated on August 22, 2022

Comments

  • user1096207
    user1096207 over 1 year

    I have about 2200 different files in a few different folders, and I need to rename about about 1/3 of them which are in their own subfolder. Those 700 are also in various folders as well.

    For example, there might be The top-most folder is Employees, which has a few files in it, then the folder 2002 has a few, 2003 has more files, 2004 etc.

    I just need to attach the word "Agreement" before the existing name of each file. So instead of it just being "Joe Schmoe.doc" It would be "Agreement Joe Schmoe.doc" instead.

    I've tried googling such scripts, and I can find stuff similar to what I want but it all looks completely foreign to me so I can't understand how I'd modify it to suit my needs.

    Oh, and this is for windows server '03.

  • user1096207
    user1096207 over 12 years
    See, now, looking at that that makes 100% logical sense to me and I can read through it and follow it no problem. Would I have been able to write that? No. Also, whatever I found online yesterday looked like moonspeak to me and nothing like that. About to try it in a moment. edit; Beautiful! Thank you so much!