Running JScript in a CMD File


I do a lot of work from a command shell and this is a little hack that I threw together to avoid having to type out "cscript" every time I wanted to run some specific JScript (aka JavaScript) file. Using some arcane aspects of the two languages, it is possible to embed the JScript code in a .cmd file which the command shell can initially execute and then pass off to cscript.exe for the heavy lifting. Below is a sample file, if it all makes sense you can stop reading now, otherwise continue on to see what is happening.


 


test.cmd



1      @set @cmdinterop=1 /*


2      @echo off


3      set @cmdinterop=


4      cscript //E:JSCRIPT "%~dpnx0" %*


5      goto :eof


6      */


7     


8      var today = new Date();


9     


10     WScript.Echo(today);


 


The main thing to remember is that the whole file is parsed by both cmd.exe and then cscript.exe. With that key piece of information let's step through the file.


CMD.EXE



1      @set @cmdinterop=1 /*


CMD sees the @ symbols which means "no echo", and then creates a new environment variable named "@cmdinterop" and sets its value to "1 /*". "@cmdinterop" is just a random name we've picked which has no special meaning in and of itself, though the 'at' symbol is important; more information on this will come later.


 



2      @echo off


Turn off future echoing of commands.


 



3      set @cmdinterop=


Nuke the environment variable we just set. Someone looking at this might be thinking: not the most efficient CMD script. If we don't need or use the environment variable, why do we create it? The reason is because the first line needs to be a benign statement in both languages: CMD and JScript. It just so happens that "@set" has meaning in both languages and so we make use of that fact to do our dirty work. This line will just clean up what we had to do on line 1, with regard to CMD's processing.


 



4      cscript //E:JSCRIPT "%~dpnx0" %*


We now have the CMD script invoke cscript.exe and pass in the name of the current file. The "%~…0" syntax is the cryptic way to get at the file-system properties for the current executing script: "d" for drive letter, "p" for path, "n" for the filename, and "x" for the file's extension. We also attempt to pass in any additional parameters by using the "%*" syntax.


 


At this point, CMD sits and waits for cscript.exe to finish executing, but we will just continue on since we are almost done with CMD's processing:


 



5      goto :eof


CMD now jumps to the end of the file, thus bypassing all of our JScript code (lines 6+).


CSCRIPT.EXE



1      @set @cmdinterop=1 /*


In JScript, "@set @" creates a variable which can be used for conditional compilation. We don’t really care about the variable which gets created as we will never use it, but what we do care about is being able inject the start of the block comment, which will allow us to ignore all the CMD code (lines 2 through 5):



2      @echo off


3      set @cmdinterop=


4      cscript //E:JSCRIPT "%~dpnx0" %*


5      goto :eof


6      */


 


Once we skip past all the CMD code we are free to use whatever JScript code we want:



7     


8      var today = new Date();


9     


10     WScript.Echo(today);


@set


From the JScript parser, things are easy as long as you can get the start of a comment block going. The tricky part is getting that "/*" to parse in CMD without causing an error. Luckily for us, "@set" is the magic code which gives us the avenue to do just that because it can be parsed by both languages.


 


Going forward, you may want to be nice to future readers of your code and sprinkle in a few extra comments:



@set @cmdinterop=1 /* this line interpreted by both CMD and JSCRIPT


@REM Start of the CMD code


@echo off


set @cmdinterop=


cscript //E:JSCRIPT "%~dpnx0" %*


goto :eof


REM end of the CMD code


*/


 


// Start of the JScript Code:


 


 


Standard Disclaimer: As mentioned earlier, this is a hack. I've tested and used this on XP SP2, but changes to the languages and parsers may cause this script to break.


 


 

Comments (4)
  1. http://blogs.msdn.com/joshpoley/archive/2008/01/15/running-jscript-in-a-cmd-file.aspx より、 @set @cmdinterop=1 /* @echo off set @cmdinterop= cscript //E:JSCRIPT "%~dpnx0" %* goto :eof */ var today = new Date(); WScript.Echo(today);

  2. Josh Poley says:

    Andrea Giammarchi  
    I think the technique is not bad but it could be easily improved…

  3. LittleBobby says:

    If you don't like setting a variable, then you can use a header like the following:

    @if (false)==(WSH JScript) rem /*

    @echo off

    cscript.exe //NoLogo //E:JScript "%~dpnx0" %*

    goto :eof

    */

    @end //JScript starts here

    JSCript comment marks (/* and */) are not mandatory, but I write them in order to force the syntax colorer to highlight the CMD part as a comment.

  4. Peter Schwier says:

    I managed to reduce it to a oneline at the begining of the file.

    @if (false)==(false) @cscript //nologo //e:jscript "%~dpnx0" %* & goto :eof @end

    /* The first line of this file needs to remain unchanged.

    * It holds a preamble which will run the current file as

    * a JScript WSH script despite having a .bat extension.

    * Documenation:

    * msdn.microsoft.com/…/58dz2w55%28VS.85%29.aspx

    * blogs.msdn.com/…/running-jscript-in-a-cmd-file.aspx

    * The @if and @end are Case Sensitive!!

    * @if (condition) statements @end is JScript conditional compilation

    * @if (text1)==(text2) COMMAND is CMD Batch contitinal exection

    * The trick is that JScript will fail the test (false)

    * and ignore everything between @if and @end.  The test does not matter

    * for CMD Batch because the command REM /* is valid.

    */

Comments are closed.

Skip to main content