Ever since I read Don's post the other day pointing out the often-overlooked support for a powerful command-line scripting languge in Windows (Javascript), I've been meaning to post this. It's a batch file that will let you run JScript.NET files interactively. Since Javascript is a compiled language, it does this by compiling it to a temporary directory first and then running the compiled EXE. While this might sound slow, as it turns out it's quite acceptable for quick, scripting-type tasks, and it gives me what I've always wanted: a convenient scripting language with full, natural-sytnax access both to COM and the .NET libraries.
Much as I'd like to claim credit for the batch file, I can't. It was written by John Elliott (for whom I don't have a URL) and posted to the WinTech Off-Topic List. The only modification I made was to add the /fast- switch to the compile, which turns off “fast mode”. Near as I can tell (not yet being that familiar with JScript.NET), it makes the code somewhat slower, but enables a lot of the more “scripty” features of the language, such as the ability to add methods dynamically to BCL types. Anyway, here's the script. I called mine rjs.cmd, and stuck it in my \bin directory, which is in my path. Then I can launch scripts by simply running “rjs foo.js“.
@echo OFF
rem ///////////////////////////////////////////////////////////////////////
rem // js-net-script.cmd
rem // ====================================================================
rem //
rem // Compiles and runs a JScript.NET source file.
rem //
goto :INIT
rem ///////////////////////////////////////////////////////////////////////
rem // HELP procedure
rem // ==============
rem //
rem // Display brief help message
rem //
:HELP
if defined TRACE %TRACE% [proc %0 %*]
echo Compiles and runs a JScript.NET source file as a console application.
echo.
echo JS-NET-SCRIPT script [args]
echo.
echo script Path to the JScript.NET file you wish to compile and run.
echo args Any arguments you wish to pass to the JScript.NET app.
echo.
goto :EOF
rem ///////////////////////////////////////////////////////////////////////
rem // MAIN procedure
rem //
:MAIN
if defined TRACE %TRACE% [proc %0 %*]
rem // make sure our working directory exists.
rem // this is where our build outputs will be stored temporarily.
if not exist "%APPDATA%\js-net-script" mkdir "%APPDATA%\js-net-script"
rem // determine the various path information we'll need to compile
rem // and run our script.
set _ScriptPath=%APPDATA%\js-net-script\%~nx1
set _ExePath=%_ScriptPath%.exe
set _LogPath=%_ExePath%.log
rem // copy the script to ensure it is on a local disk.
copy "%~f1" "%_ScriptPath%" >nul 2>&1
if errorlevel 1 set ERR=%errorlevel% && goto :ERROR
rem // compile the script. we use a debug build so that we can
rem // break into the debugger. we also log the build results to a
rem // log file, so that we can show them to the user if there is
rem // a problem.
%SYSTEMROOT%\Microsoft.NET\Framework\v1.1.4322\jsc.exe /out:"%_ExePath%" /target:exe /debug+ /fast- /nologo "%_ScriptPath%" > "%_LogPath%" 2>&1
if errorlevel 1 set ERR=%errorlevel% && goto :ERROR
rem // run the application, passing in arguments.
"%_ExePath%" %*
rem // delete the local copy of the script, and all build outputs.
del /s /q "%_ScriptPath%*" >nul 2>&1
goto :EOF
rem ///////////////////////////////////////////////////////////////////////
rem // INIT procedure
rem //
:INIT
rem @if not "%ECHO%"=="" echo %ECHO%
@if not "%OS%"=="Windows_NT" goto DOSEXIT
rem Set local scope and call MAIN procedure
setlocal & pushd & set RET=
set SCRIPTNAME=%~n0
set SCRIPTPATH=%~f0
if "%DEBUG%"=="1" (set TRACE=echo) else (set TRACE=rem)
if /i {%1}=={/help} (call :HELP %2) & (goto :HELPEXIT)
if /i {%1}=={/?} (call :HELP %2) & (goto :HELPEXIT)
:INIT_MAIN
set _SAVED_PROMPT=%PROMPT%
set PROMPT==$g
call :MAIN %*
set PROMPT=%_SAVED_PROMPT%
:HELPEXIT
popd & endlocal & set RET=%RET%
goto :EOF
rem ///////////////////////////////////////////////////////////////////////
rem // ERROR procedure
rem // ===============
rem //
rem // Abnormal exit. Expects the errorlevel has been stored in %ERR%.
rem //
:ERROR
if defined TRACE %TRACE% [ERROR! %0 %*]
echo.
echo WARNING: Script failed with errorlevel: %ERR%
echo.
echo Current directory is:
echo %CD%
echo Command-line was:
echo %CMDCMDLINE%
echo.
rem // show the build log. note: we'll show it in the console, but we
rem // could also popup notepad.exe
echo Compiler output:
echo.
type "%_LogPath%"
echo.
rem notepad "%_LogPath%"
rem // give the user a chance to see the results.
rem // note: this is not necessary if we open the log file in notepad.
rem // note: while paused here, you can copy the build outputs from
rem // the working directory in %APPDATA% to run ildasm, etc.
pause
rem // clean up all the build outputs.
del /s /q "%_ScriptPath%*" >nul 2>&1
goto :EOF
rem ///////////////////////////////////////////////////////////////////////
rem // DOSEXIT
rem // ====================================================================
rem //
rem // These must be the final lines in the script.
rem //
:DOSEXIT
echo This script requires Windows NT or later.
rem ///////////////////////////////////////////////////////////////////////
I tried this using v2.0.50215 on 'foo.js', and got a crash in foo.js.exe:
ReplyDeleteEventType : clr20r3 P1 : foo.js.exe P2 : 0.0.0.0 P3 : 42ae1967
P4 : microsoft.jscript P5 : 8.0.0.0 P6 : 4257531d P7 : 928
P8 : 2f P9 : microsoft.jscript.jscript
Well, I guess that's to be expected with beta software. :p
ReplyDeleteNot quite sure what to tell you here. What happens if you compile it and run it manually?
One of my favorites parts of the discussion was John's discovery of the LickArgumentsIntoShape method in a stack trace....
ReplyDeleteJohn is quite often both intentionally and unintentially amusing. :)
ReplyDelete