Page 1 of 1
Help creating and implementing "switches" or "options" in custom batch script
Posted: 01 Nov 2017 10:13
by Cornbeetle
Is there any documentation/discussion, either in DosTips or elsewhere, on how to implement your own switches/options in a batch script? Such as, the best way to validate a switch inputted by the user, how to test which switch is used, etc...
Thanks
Re: Help creating and implementing "switches" or "options" in custom batch script
Posted: 01 Nov 2017 10:32
by Squashman
Not sure if this is what you are looking for.
foolproof counting of arguments
Re: Help creating and implementing "switches" or "options" in custom batch script
Posted: 01 Nov 2017 12:08
by Cornbeetle
Not so much validation, but simply an efficient reliable way to check with switches were used and then perform the appropriate action based on the user's input. Example: a custom script to search for file on a computer, where the user appends "/x" which means the user wants to export the search results. ...what's the best way to handle that switch to execute the code associated with that option?
Re: Help creating and implementing "switches" or "options" in custom batch script
Posted: 01 Nov 2017 12:30
by dbenham
Check out
my StackOverflow answer to a question about
Windows Bat file optional argument parsing.
I have successfully used that technique in a number of scripts, including my
JREPL.BAT regular expression find and replace utility
Dave Benham
Re: Help creating and implementing "switches" or "options" in custom batch script
Posted: 01 Nov 2017 12:56
by Squashman
Yes. I was actually going to post the link to your old GetTimeStamp.bat, which I believes uses that same technique. Your solution on SO, seems like the better answer. There is too many possibilities for failure in the selected answer.
Re: Help creating and implementing "switches" or "options" in custom batch script
Posted: 01 Nov 2017 13:03
by Cornbeetle
Awesome, I shall check out that SO answer.
Re: Help creating and implementing "switches" or "options" in custom batch script
Posted: 04 Nov 2017 12:24
by Pyprohly
I think I have a good solution going.
Here’s the parameter system I use, or at least what I would use if I ever had any good ideas for large projects
Code: Select all
::: !my_cmd! first second [third] [/key:value] [/nintendo]
:::
::: Synopsis:
::: These auxiliary functions streamline the process of argument parsing and validation.
:::
::: Description:
::: The heavy lifting is done by an external temporary JScript file. It processes the
::: batch arguments by spitting out an organised copy of each of the positional and
::: optional arguments into their own separate variables. This means you can specify
::: arguments in any order on the command line without having to worry about separating
::: positional arguments from optional arguments.
:::
::: The original %1 variables are preserved. Denote named arguments with either '/' or
::: '-'. For key-value pairs use ':' or '=' as the delimiter.
:::
@echo off
setlocal EnableDelayedExpansion
goto :main
:arg_parser FileName
if "%~1"=="" exit /b 5
set "_arg_file21234="%~1""
if not exist %_arg_file21234% exit /b 53
for /f "tokens=1,* delims==" %%I in (' 2^>nul set arg[ ') do (set "%%~I=")
set "arg[]=1"
<%_arg_file21234% set /p "arg[*]="
setlocal EnableDelayedExpansion
>%_arg_file21234% echo var args=WScript.Arguments;for^(var argkList=[],pC=0,nC=0,i=0;i^<args.Length;i++^){var arg=args.Item^(i^);if^(^^!arg.length^|^|'/-'.indexOf^(arg.charAt^(0^)^)^<0^){WScript.Echo^('arg['+ ++pC+']='+arg^)}else{var a=arg.indexOf^(':'^),b=arg.indexOf^('='^);var d=a*b^<0?Math.max^(a,b^):Math.min^(a,b^);argk=^(d^<0?arg.slice^(1^):arg.slice^(1,d^)^).toLowerCase^(^);argv=d^<0?'1':arg.slice^(d+1^);WScript.Echo^("arg['"+argk+"']="+argv^);if^(^^!^(new RegExp^('^^^^(?:'+argkList.join^('^|'^)+'^)$'^).test^(argk^)^)^){argkList.push^(argk^);nC++}}}WScript.Echo^('arg[#]='+i+'\narg[#p]='+pC+'\narg[#q]='+nC^)
endlocal
for /f "delims=" %%I in (' cscript.exe /nologo %_arg_file21234% %arg[*]% ') do (set %%I)
del %_arg_file21234%
set "_arg_file21234="
goto :eof
:arg_validate
setlocal EnableDelayedExpansion
REM Example argument validation:
REM Accept 2-3 positional arguments, an optional 'key' key-value pair and an optional 'nintendo' switch
if not defined arg[] exit /b 5
if not defined arg[1] echo ArgErr: takes from 2-3 positional arguments but none were given& exit /b 1
if not defined arg[2] echo ArgErr: takes from 2-3 positional arguments but 1 was given& exit /b 1
if defined arg[4] echo ArgErr: takes from 2-3 positional arguments but %arg[#p]% were given& exit /b 1
set "expected_keys=key:nintendo"
for /f "useback tokens=1,* delims==" %%I in (` 2^>nul set arg[' `) do (
set "argk=%%~I"
set "argk=!argk:~5,-2!"
echo(!argk!| findstr "^%expected_keys::=$ ^%$" >nul || (
echo ArgErr: got an unexpected named argument '!argk!'
exit /b 1
)
)
endlocal
exit /b 0
:help
setlocal EnableDelayedExpansion
set "my_cmd=%~n0"
for /f "tokens=1,* delims=:" %%1 in (' findstr /n "^:::" "%~f0" ') do echo(%%2
endlocal
goto :eof
:main
set "arg[0]=%temp%\%~nx0_arg-parse.tmp.js"
>"%arg[0]%" echo(%* & call :arg_parser "%arg[0]%"
if defined arg['?'] call :help & exit /b 0
call :arg_validate >&2 || exit /b 1
REM Batch program starts here
echo Argument count: %arg[#]% &REM this does not necessarily equal %arg[#p]% + %arg[#q]%. (Hint: consider duplicate switches)
echo Positional argument count: %arg[#p]%
echo Named argument count: %arg[#q]% &REM number of unique named arguments given
echo All args: %arg[*]% &REM should be the same as %*
for /l %%I in (1 1 %arg[#p]%) do echo Argument %%I is !arg[%%I]!
if defined arg['key'] echo The named argument 'key' = %arg['key']%
echo(
set arg[
The boilerplate is rather bulky, but I would expect nothing less than heavy-duty if you’re looking into clean argument handling.
This is this the non-minified JScript code.
Code: Select all
var args = WScript.Arguments
for (var argkList = [], pC = 0, nC = 0, i = 0; i < args.Length; i++) {
var arg = args.Item(i)
if (!arg.length || '/-'.indexOf(arg.charAt(0)) < 0) {
WScript.Echo('arg[' + ++pC + ']=' + arg)
} else {
var a = arg.indexOf(':'), b = arg.indexOf('=')
var d = a * b < 0 ? Math.max(a, b) : Math.min(a, b)
argk = (d < 0 ? arg.slice(1) : arg.slice(1, d)).toLowerCase()
argv = d < 0 ? '1' : arg.slice(d + 1)
WScript.Echo("arg['" + argk + "']=" + argv)
// ignore duplicate switches
if (!(new RegExp('^(?:' + argkList.join('|') + ')$').test(argk))) {
argkList.push(argk)
nC++
}
}
}
WScript.Echo(
'arg[#]=' + i +
'\narg[#p]=' + pC +
'\narg[#q]=' + nC
)
Example usage:
Code: Select all
C:\>file.bat near /key:"value"
ArgErr: takes from 2-3 positional arguments but 1 was given
C:\>file.bat near far /key:"value"
Argument count: 3
Positional argument count: 2
Named argument count: 1
All args: near far /key:"value"
Argument 1 is near
Argument 2 is far
The named argument 'key' = value
arg[#p]=2
arg[#q]=1
arg[#]=3
arg['key']=value
arg[*]=near far /key:"value"
arg[1]=near
arg[2]=far
arg[]=1
C:\>