Efficient use of JScript expressions in Batch files

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Efficient use of JScript expressions in Batch files

#1 Post by Aacini » 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:

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

Post Reply