3

There are situations when it is important to identify whether double quotes are passed as arguments to a WSH script. For example because they should be passed to another executable to be run.

The standard parsing functions/objects:

objArgs = WScript.Arguments;
for (i = 0; i < objArgs.length; i++)
{
   WScript.Echo(objArgs(i));
}

do not differentiate between:

cscript foo.js  "bar"

and

cscript foo.js  bar

Is it possible with some other approach?

Note: I also tried to sort of escape them with several combinations like:

cscript foo.js  '"bar"'

It seems that they are simply stripped away.

antonio
  • 10,629
  • 13
  • 68
  • 136

1 Answers1

3

Following @Ekkehard.Horner suggestions:

Solution

// parseArgs.js     
// Parsing jscript script arguments verbatim 

var Shell =  new ActiveXObject("WScript.Shell"),
    wmi  = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2"),
    guid = (new ActiveXObject("Scriptlet.TypeLib")).GUID.substring(0,38),
    windir=Shell.ExpandEnvironmentStrings("%WinDir%"),
    winver="\"" + windir +  "\\System32\\winver.exe\" " + guid,
    pcol, pid, cmd;


// Run winver.exe hidden and get this script ID as its ParentProcessId 
winver=winver.replace(/\\/g, "\\\\");
Shell.Run("winver " + guid, 0);
pcol = new Enumerator (wmi.ExecQuery( 
  "SELECT * From Win32_Process WHERE CommandLine='"+ winver + "'",
  "WQL", 32));
for (; !pcol.atEnd(); pcol.moveNext()){
  var prc = pcol.item();
  pid=prc.ParentProcessId;
  prc.Terminate;
}


// Get the command line for the found PID 
pcol = new Enumerator (wmi.ExecQuery( 
  "SELECT * From Win32_Process WHERE ProcessID="+ pid,
  "WQL", 32));
for (; !pcol.atEnd(); pcol.moveNext()){
  var prc = pcol.item();
  cmd =prc.CommandLine;
}
WScript.Echo(cmd);

// Parse command line for arguments
var ags,
    parseCmd=function(cmd){// WMI trims initial spaces
      var p = new Object(),
          re =/^"/.test(cmd) ? /"[^"]+" */ : /\S+\s*/;
      p.nxt=re.test(cmd) ? cmd.match(re)[0] : ""; // extract next token
      p.rst=cmd.replace(re, "")                 ; // remainder     
      return(p);
    }


// Strip c/wscript path 
ags=parseCmd(cmd).rst
//WScript.Echo(ags);

// Remove WSH "//xxx" options 
ags=ags.replace(/\/\/\w+ +/g, "")   
//WScript.Echo(ags);

// Strip script name and get arguments
ags=parseCmd(ags).rst
WScript.Echo(ags);

// Loop args and store as an array 
var i=1, aags=[];
while(ags != ""){
  var p =parseCmd(ags);
  ags=p.rst;
  aags.push(p.nxt.replace(/ +$/, ""));
  WScript.Echo(i, p.nxt);
  i++;
}
WScript.Echo(aags);

Test

Running parseArgs.js gives:

> cscript //nologo parseArgs.js "hello" world
cscript  //nologo parseArgs.js "hello" world
"hello" world
1 "hello"    
2 world
"hello",world

The line:

> parseArgs.js "hello" world

gives similar results.

Comments

Do we need such a convoluted script? Short answer: no. Long: depends.

In general, assuming you know the name of your script when it is run, you could query WMI for it.
Anyway, when you deploy your script, you do not normally have control on the deploy directory. So, if there is another script running under the same name, you can't know for sure which one is yours.
Another not so edge case is when there are two or more instances of your script running.

The strategy here is to run some dummy standard Windows executable (winver.exe) hidden, passing to it a GUID. In this way, it is safe to identify winver.exe command line by the unique GUID and consequently your script as the parent of winver.exe.
winver.exe does not require arguments, but does not protest if you pass some to it.

antonio
  • 10,629
  • 13
  • 68
  • 136