14 December, 2011

Hate bat scripting? Me too...



Abstract
Hate BAT files? Yeah, so do I. Stuck on windows for reasons beyond your control but need to do some scripting? Sure, you can use perl, ruby, python or jruby, but all of those require the installation of an interpreter.

How do you survive if you have a bare-metal windows box and aren't allowed to dictate prerequisites? You could fall back on bat scripting and risk the integrity of your hairline or you could fall back on your JavaScripting skills and go against the WSH.

I'll show you a couple of minor examples in a JS coding idiom that makes sense to me and share as many of the references as I remember that helped me with my code. Lastly, I'll give a few suggestions as to where this can be used. Hopefully, this will be enough to spur your imagination to use JavaScript solve problems you have on the windows platform.

Windows Scripting Host
Here's your goto microsoft site for this http://msdn.microsoft.com/en-us/library/9bbdkx3k.aspx . This site is going to give you all of the conceptual stuff you need to know about this. I'll talk about some things I had to reconcile and give you a codesample walkthrough.

Executing your scripts


  1. just execute a js script from the cmd line
    jsmn_all.js
  2. execute it from the command line with cscript
    cscript /Nologo jsmn_all.js
  3. create a .wsf file to define and execute it with cscript
    cscript /Nologo jsmn.wsf
  4. or just execute your wsf file from the command line
    jsmn.wsf
wsf? WTF?
Go here for info on wsh files. Basically, they offer the ability to define a script and do imports, similar to what you do in an html file with

I'm not going to get too far ahead of my self decomposing this entire script. Suffice it to say that this allows you to connect to jsmn.js and use things in it, such as the ScriptHelper object. Objects that are part of the windws scripting host, e.g., the WScript object are going to be in your execution scope no matter what, so there's no need to root around for some WSH javascript file to "include."

You can have multiple script references, but I don't know the limitations. You can even have your script entry point in one of the referenced scripts. I can see where it might make better sense (and make your editor of choice happier) if you put all logic in a js file and only used hour WSH file like a project file.

What can you do with this here WSH thingy?
Good question. Well, I have a progressive "hello jsmn" demo for you that might help to enlighten. You'll really have to dig through the Microsoft pages to see all that you can do. You can even use this to wrap windows commandline commands.

I see it as a great way to drop a toolkit onto a bare metal windows box and make yourself productive. Seems like something that could be very powerful for sysadmins or testing engineers especially for windows VMs.

Oh yeah, the demo
  

Running just this with no Arguments yields a dialog box with:Needs two arguments: one to specifiy the subject and another for desired action. Here's the ScriptHelper class we instantiate and call methods from:

/*
 * Class for grouping functions that are important to this script.
 */
function ScriptHelper(word1,word2) {

    this.desktopiconfolder = "";
    this.envname = "jsmn";
    this.write_script;
    this.fullFileName = WScript.ScriptFullName;

    this.wshShell = WScript.CreateObject("WScript.Shell");
    this.objFSO = WScript.CreateObject("Scripting.FileSystemObject");

    this.wordOne = word1;
    this.wordTwo = word2;

    this.speak = function() {
        WScript.echo(this.wordOne + " " + this.wordTwo);
    }

}

That's all well and good but...
I'd like to do something slightly more sophisticated than just echo some crap out

So, let's write to a file. I'll add a speakToFile function to my ScriptHelper class


this.speakToFile = function(loc) {

        this.write_script = loc;

        var filelocation = loc;
        if ( this.objFSO.fileExists(filelocation) ) {
            this.objFSO.moveFile(filelocation,filelocation + ".bak");
        }
        var ForWriting = 2;
        propFile = this.objFSO.OpenTextFile(filelocation,ForWriting,true);
        propFile.WriteLine("WScript.echo('from file:" + this.wordOne + " " + this.wordTwo + "');");
        propFile.Close();
    }

 and tell my WSF file to call that instead of speak.
  
Now, when I run

jsmn.wsf hellooooo jsmn

it writes a file to disk called jsmn_write.js. This file contains one line of javascript and if I execute jsmn_write.js, I'll see

"hellooooo jsmn"

So, I can write a file, what else?
There's all sorts of windowsey stuff you can do like write shortcuts... So, I add a call to a createShortcuts() method

And, this takes a few more methods to support it, but I add the following methods to ScriptHelper

this.createShortcuts = function() {
        this.createShortcutFolder("Shortcuts for " + this.envname);
        this.createShortcut("TestShortcutFor-" +
                this.envname,this.getScriptDir()+"\\"+this.write_script,this.env
name);

    };

this.createShortcutFolder = function(name) {
        var desktop = this.wshShell.SpecialFolders("Desktop");
        this.desktopiconfolder = desktop + "\\" + name;
        this.objFSO.CreateFolder(this.desktopiconfolder);
    };

this.getScriptDir = function() {
        var forShortening = this.fullFileName;
        var lastSlash = forShortening.lastIndexOf("\\");
        var shortened = forShortening.substring(0,lastSlash);

        return shortened;
    };
    this.createShortcut = function(linkName,tgtPath,args) {
        var myShort = this.wshShell.CreateShortcut(this.desktopiconfolder +
                        "\\"+linkName+".lnk");
        myShort.WindowStyle = 1;
        myShort.TargetPath = tgtPath;
        myShort.Arguments = args;
        myShort.Save();
    };

Now when I run

jsmn.wsf hellooooo jsmn

I get a shortcut folder on my desktop with a shortcut in it that has a link to jsmn_write.js. That's it for my demo, I hope it was simple enough to follow but sophisticated enough to give you some ideas for new ways to use your javascripting skillz.


Debugging?
cscript has an option for a debugger, but I have not successfully used it.
//X         Execute script in debugger
NOTE: I will research this further and update or write a new entry on this

Soo... What else is possible?
Unit testing is pretty cool and if you're developing some sort of a toolkit with WSH scripts, you might want to be able to build them with TDD or just have some tests to prove that your code doesn't toally suck. I have not tried either of these, but they look very promising:


The next time someone tells you that JS is a toy language and you can't do anything with it, you can tell them to kiss your asterisk.

Acknowledgements
Also, this post uses the awesome syntax highlighter from alex gorbatchev it's some pretty cool javascripting in its own right.

No comments: