Help creating and implementing "switches" or "options" in custom batch script

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Cornbeetle
Posts: 31
Joined: 23 Nov 2015 09:34

Help creating and implementing "switches" or "options" in custom batch script

#1 Post by Cornbeetle » 01 Nov 2017 10:13

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 :!:

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Help creating and implementing "switches" or "options" in custom batch script

#2 Post by Squashman » 01 Nov 2017 10:32

Not sure if this is what you are looking for.
foolproof counting of arguments

Cornbeetle
Posts: 31
Joined: 23 Nov 2015 09:34

Re: Help creating and implementing "switches" or "options" in custom batch script

#3 Post by Cornbeetle » 01 Nov 2017 12:08

Squashman wrote:Not sure if this is what you are looking for.
foolproof counting of arguments
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?

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Help creating and implementing "switches" or "options" in custom batch script

#4 Post by dbenham » 01 Nov 2017 12:30

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

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Help creating and implementing "switches" or "options" in custom batch script

#5 Post by Squashman » 01 Nov 2017 12:56

dbenham wrote:Check out this StackOverflow answer to a question about Windows Bat file optional argument parsing.
Dave Benham
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.

Cornbeetle
Posts: 31
Joined: 23 Nov 2015 09:34

Re: Help creating and implementing "switches" or "options" in custom batch script

#6 Post by Cornbeetle » 01 Nov 2017 13:03

Awesome, I shall check out that SO answer.

Pyprohly
Posts: 3
Joined: 24 Dec 2014 22:48

Re: Help creating and implementing "switches" or "options" in custom batch script

#7 Post by Pyprohly » 04 Nov 2017 12:24

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 :cry:

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:\>

Post Reply