2

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!

5
  • Is there any particular reason you didn't use Python or CMD.EXE for this task? I think either would be easier... Commented Jun 28, 2012 at 0:27
  • I suppose my real question is how would I go about accessing that Python command or CMD.EXE command if I was in a different directory in the command prompt than where the Python/CMD.EXE file was located; in other words how to I make it universal? Commented Jun 28, 2012 at 0:34
  • @sarnold - I don't agree. There's nothing wrong with using jscript/WSH to script sysadmin things on Windows. Commented Jul 2, 2012 at 21:32
  • @Cheeso: I'm just surprised that running a Python program with arguments is being handed to a different tool entirely. There's nothing wrong with it, sure, but I like to reduce the amount of dependencies any given application / solution uses -- even to the point of continuing to maintain old Perl scripts even when I know I'd rather re-write the whole thing in Ruby, just because I don't want to push a new dependency upon people without good reason. Commented Jul 2, 2012 at 22:28
  • of course. I may have misunderstood. If the goal is to run a python program, then.... why not just run the python app? But if the goal is to script general administrative tasks, then jscript+WSH seems like a reasonable alternative. Cheers. Commented Jul 2, 2012 at 22:43

2 Answers 2

4

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);
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.