I've got a simple question:
What's the best way to execute a single WshShell command from a Windows batch (.bat) script?
(hopefully it's not creating a new file with VB code)
You can access WshShell via VBScript or Jscript. Both can be embedded within a batch file, but JScript is much cleaner.
Most people execute VBScript within batch by writing a temporary VBS file. But it is possible to do it without a temporary file. See Is it possible to embed and execute VBScript within a batch file without using a temporary file? for various options.
Embedding JScript within batch is quite easy. See https://stackoverflow.com/a/5656250/1012053. I use a very slight variation of that technique.
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: ******* Begin batch code *********
@echo off
:: Your batch logic goes here
:: At any point you can execute the following to access your JScript
cscript //E:JScript //nologo "%~f0" yourJscriptParametersGoHere
:: Be sure to terminate your script so that
:: it does not fall through into the JScript code
exit /b
********* Begin JScript code **********/
var WshShell=WScript.CreateObject("WScript.Shell")
/* do whatever with your WshShell object */
Explanation:
The key to the technique is the first line. It must be a line that has valid syntax for both JScript and batch.
Batch sees the first line as a simple IF command that always evaluates to false, so it never executes the non-existent @end command, and no harm is done. The following lines are all normal batch code until exit /b is reached, at which point batch processing terminates and the remaining lines are ignored.
JScript sees the first line as an empty conditional compilation block, followed by the beginning of a multi-line comment. JScript ignores the following batch code because it is all part of the comment. The comment ends with */
, and then normal JScript code follows.
The only thing that could fail is if your batch code must have */
within it, because that would terminate the JScript comment prematurely. But that can be solved by putting something between the *
and /
that disappears after batch parsing. If the code is not quoted, then you can simply escape the slash as follows: *^/
. If the code is quoted, then you can expand an undefined variable: *%=%/
. A variable named =
is guaranteed not to be defined.
This is the method I use to write a Batch-JScript hybrid script:
@if (@CodeSection == @Batch) @then
:: The first line above is...
:: in Batch: a valid IF command that does nothing.
:: in JScript: a conditional compilation IF statement that is false,
:: so this section is omitted until next "at-sign end".
@echo off
rem EXPR.BAT: Evaluate a JScript (arithmetic) expression
rem Antonio Perez Ayala
rem Define an auxiliary variable to call JScript
set JSCall=Cscript //nologo //E:JScript "%~F0"
rem Do Batch business here, for example:
%JSCall% %1
goto :EOF
End of Batch section
@end
// JScript section
WScript.Echo(eval(WScript.Arguments.Unnamed.Item(0)));
For example:
EXPR 1/3
EDIT: If you want a simpler/shorter method, use this one:
@set @a=0 /*
@cscript //nologo //E:JScript "%~F0" "%~1"
@goto :EOF */
WScript.Echo(eval(WScript.Arguments(0)));
Again, the first @set @a=0 /*
is a valid statement/command in both JScript and Batch that is only used to insert the start of a JScript comment (/*), so the Batch section be ignored by JScript. The comment is closed (*/) after the final goto :EOF
.
EDIT: An even simpler method...
@set @a=0 // & cscript //nologo //E:JScript "%~F0" "%~1" & goto :EOF
WScript.Echo(eval(WScript.Arguments(0)));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With