Efficient use of JScript expressions in Batch files
Posted: 02 Sep 2015 13:56
Although the method to get the result of a JScript expression in a Batch variable is well known, such method is somewhat tricky and inefficient because it requires to execute CScript.exe command (and compile the Batch-JScript hybrid file) each time that a JScript expression is required in the Batch file.
As a result of a requirement in my new Batch-BGI graphics library, a very efficient method to get these results was developed. This method compile/execute the JScript code just once and then it is kept in an efficent wait state until the Batch code request a result from it. I used this method in a wrapper routine that allows to get JScript results in Batch variables in a way rather similar to the original SET command, using a subroutine with the :SET name. For example:
If the /A switch is included in CALL :SET subroutine, several variables may be assigned in the same invocation:
An additional advantage of this method is that the JScript code remains active between Batch requests. This means that is possible to create a JScript variable from the Batch code and use it in posterior invocations of the :SET subroutine:
We may make good use of JScript object oriented features and eval() method to create any type of JScript variable from the Batch code, like an array or a function.
The code below contain the :SET subroutine and a small program as example of how to use the previously described features:
This method can be even more efficient if the :SET subroutine is not used, but the "send" and "setVars" variables are prepared in advance just one time, and then use they in the loop instead of "call :set" subroutine invocation.
Antonio
As a result of a requirement in my new Batch-BGI graphics library, a very efficient method to get these results was developed. This method compile/execute the JScript code just once and then it is kept in an efficent wait state until the Batch code request a result from it. I used this method in a wrapper routine that allows to get JScript results in Batch variables in a way rather similar to the original SET command, using a subroutine with the :SET name. For example:
Code: Select all
call :set str = "This is a string".toUpperCase()
If the /A switch is included in CALL :SET subroutine, several variables may be assigned in the same invocation:
Code: Select all
call :set /A rad = 180/Math.PI
call :set /A sin = Math.sin(%x%/%rad%), cos = Math.cos(%x%/%rad%), tan = Math.tan(%x%/%rad%)
An additional advantage of this method is that the JScript code remains active between Batch requests. This means that is possible to create a JScript variable from the Batch code and use it in posterior invocations of the :SET subroutine:
Code: Select all
rem Create "rad" variable in the JScript code:
call :set /A dummy = rad = 180/Math.PI
rem Use it:
call :set /A sin = Math.sin(%x%/rad), cos = Math.cos(%x%/rad), tan = Math.tan(%x%/rad)
We may make good use of JScript object oriented features and eval() method to create any type of JScript variable from the Batch code, like an array or a function.
The code below contain the :SET subroutine and a small program as example of how to use the previously described features:
Code: Select all
@if (@CodeSection == @Batch) @then
@echo off
rem Example of use of :SET subroutine
rem This part start the JScript engine; keep it as is
if "%~1" equ "Main" goto %1
cd . > JSOutput.txt
"%~F0" Main 3>&1 1>&2 4<JSOutput.txt | CScript //nologo //E:JScript "%~F0" >> JSOutput.txt
timeout /T 1 /NOBREAK > NUL
del JSOutput.txt
goto :EOF
:Main
rem Main program code goes here
setlocal EnableDelayedExpansion
cls
rem Numeric example
echo/
echo TABLE OF TRIGONOMETRIC FUNCTIONS
echo/
echo Deg Sin Cos Tan
echo/
call :set dummy = rad = 180/Math.PI
for /L %%x in (0,15,360) do (
call :set /A sin = "Math.sin(%%x/rad)", cos = "Math.cos(%%x/rad)", tan = "Math.tan(%%x/rad)"
set "x= %%x"
for %%v in (sin cos tan) do set "%%v=!%%v! "
echo !x:~-3! !sin:~0,24!!cos:~0,24!!tan:~0,24!
)
rem String example
echo/
rem Create "str" JScript variable:
call :set str = str = "The quick brown fox jump over the lazy dog"
echo %str%
rem Use its value:
call :set str = str.toUpperCase()
echo %str%
rem Create "pos" JScript function:
call :set dummy = pos = function(re){res="";while(match=re.exec(str))res+=","+match.index;return(res.substr(1))}
REM echo %dummy%
rem Use it:
set "letter=o"
call :set pos = pos(/%letter%/ig)
echo Positions of letter "%letter%": %pos%
goto :EOF
SET subroutine assign to Batch variables the value of JScript expressions
Antonio Perez Ayala aka Aacini
call :set var = JScript expression
call :set /A var1 = "expr1", var2 = "expr2" ...
Numeric operands in the expressions can NOT be Batch variables, just %numbers%,
so you must enclose they between parentheses in order to avoid syntax errors
if the value of a variable may have negative sign.
:set
if /I "%~1" equ "/A" goto set/A
set send=%*
set send=%send:* =%
if "%send:~0,1%" equ "=" set send=%send:~1%
echo %send%>&3
set "%1="
:get
set /P "%1=" <&4
if not defined %1 goto get
exit /B
:set/A
shift
set N=0
set "setVars="
set "send="
:nextVar
set /A N+=1
set "setVars=%setVars%& set "%1=%%%N%" "
set "send=%send%+" "+(%~2)"
shift & shift
if "%~1" neq "" goto nextVar
echo %send:~1% >&3
set "receive="
:get/A
set /P "receive=" <&4
if not defined receive goto get/A
for /F "tokens=1-%N%" %%1 in ("%receive%") do (
%setVars:~1%
)
exit /B
@end
// JScript section
while ( ! WScript.Stdin.AtEndOfStream ) {
WScript.Stdout.WriteLine(eval(WScript.Stdin.ReadLine()));
}
This method can be even more efficient if the :SET subroutine is not used, but the "send" and "setVars" variables are prepared in advance just one time, and then use they in the loop instead of "call :set" subroutine invocation.
Antonio