lordmarinara
lordmarinara

Reputation: 277

Windows Scripting to call other commands?

I was trying my hand at Windows shell scripting using cscript and Javascript. The idea was to take a really long Python command that I was tired of typing into the command line over and over. What I wanted to do was write a script that is much shorter to write the whole Python command into a Windows script and just call the Windows script, which would be a lot less to type. I just don't know how I would go about calling a "command within a command" if that makes sense.

This is probably an easy thing, but I'm an newbie at this so please bear with me!

The idea:

Example original command: python do <something really complicated with a long filepath>

Windows Script: cscript easycommand

 <package id = "easycommand">
      <job id = "main" >
          <script type="text/javascript">
               // WHAT GOES HERE TO CALL python do <something really complicated>
               WScript.Echo("Success!");
          </script>
      </job>
 </package>

Thanks for all your help!

Upvotes: 2

Views: 798

Answers (2)

Cheeso
Cheeso

Reputation: 192487

Here's what I use

function logMessage(msg) {
    if (typeof wantLogging != "undefined" && wantLogging) {
        WScript.Echo(msg);
    }
}

// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var windowStyle = {
    hidden    : 0,
    minimized : 1,
    maximized : 2
};

// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var specialFolders = {
    windowsFolder   : 0,
    systemFolder    : 1,
    temporaryFolder : 2
};

function runShellCmd(command, deleteOutput) {
    deleteOutput = deleteOutput || false;
    logMessage("RunAppCmd("+command+") ENTER");
    var shell = new ActiveXObject("WScript.Shell"),
        fso = new ActiveXObject("Scripting.FileSystemObject"),
        tmpdir = fso.GetSpecialFolder(specialFolders.temporaryFolder),
        tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName()),
        rc;

    logMessage("shell.Run("+command+")");

    // use cmd.exe to redirect the output
    rc = shell.Run("%comspec% /c " + command + "> " + tmpFileName,
                       windowStyle.Hidden, true);
    logMessage("shell.Run rc = "  + rc);

    if (deleteOutput) {
        fso.DeleteFile(tmpFileName);
    }
    return {
        rc : rc,
        outputfile : (deleteOutput) ? null : tmpFileName
    };
}

Here's an example of how to use the above to list the Sites defined in IIS with Appcmd.exe:

var
fso = new ActiveXObject("Scripting.FileSystemObject"),
windir = fso.GetSpecialFolder(specialFolders.WindowsFolder),
r = runShellCmd("%windir%\\system32\\inetsrv\\appcmd.exe list sites");
if (r.rc !== 0) {
    // 0x80004005 == E_FAIL
    throw {error: "ApplicationException",
           message: "shell.run returned nonzero rc ("+r.rc+")",
           code: 0x80004005};
}

// results are in r.outputfile

var
textStream = fso.OpenTextFile(r.outputfile, OpenMode.ForReading),
sites = [], item, 
re = new RegExp('^SITE "([^"]+)" \\((.+)\\) *$'),
parseOneLine = function(oneLine) {
    // each line is like this:  APP "kjsksj" (dkjsdkjd)
    var tokens = re.exec(oneLine), parts;

    if (tokens === null) {
        return null;
    }
    // return the object describing the website
    return {
        name : tokens[1]
    };
};

// Read from the file and parse the results.
while (!textStream.AtEndOfStream) {
    item = parseOneLine(textStream.ReadLine()); // you create this...
    logMessage("  site: " + item.name);
    sites.push(item);
}
textStream.Close();
fso.DeleteFile(r.outputfile);

Upvotes: 4

user268396
user268396

Reputation: 11976

Well, basically:

  1. Obtain a handle to the shell so you can execute your script
  2. Create the command you want to execute (parameters and all) as a string
  3. Call the Run method on the shell handle, and figure out which window mode you want and also whether you want to wait until the spawned process finishes (probably) or not.
  4. Error handling because Run throws exceptions

At this point it's worth writing a lot of utility functions if ever you need to do it more than once:

// run a command, call: run('C:\Python27\python.exe', 'path/to/script.py', 'arg1', 'arg2') etc.
function run() {
  try {
    var cmd = "\"" + arguments[0] + "\"";
    var arg;
    for(var i=1; i< arguments.length; ++i) {
      arg = "" + arguments[i];
      if(arg.length > 0) {
    cmd += arg.charAt(0) == "/" ? (" " + arg) : (" \"" + arg + "\"");
      }
    }
    return getShell().Run(cmd, 1, true); // show window, wait until done
  }
  catch(oops) {
    WScript.Echo("Error: unable to execute shell command:\n"+cmd+ 
             "\nInside directory:\n" + pwd()+
         "\nReason:\n"+err_message(oops)+
         "\nThis script will exit.");
    exit(121);
  }
}

// utility which makes an attempt at retrieving error messages from JScript exceptions
function err_message(err_object) {
  if(typeof(err_object.message) != 'undefined') {
    return err_object.message;
  }
  if(typeof(err_object.description) != 'undefined') {
    return err_object.description;
  }
  return err_object.name;
}

// don't create new Shell objects each time you call run()
function getShell() {
  var sh = WScript.CreateObject("WScript.Shell");
  getShell = function() {
    return sh;
  };
  return getShell();
}

For your use case this may be sufficient, but you might want to extend this with routines to change working directory and so on.

Upvotes: 1

Related Questions