Page 1 of 1
Ignoring Arguments
Posted: 26 Aug 2021 13:28
by atfon
I have a batch script which can be run either interactively or through the use of command line arguments. I use switches like /a to define the argument. It works fine. It ignores if there isn't a slash "/" or "-" for the switch. It will invalidate an argument that doesn't exist. I have just been debugging to see if I could cause behavior I did not want. I noticed that if I followed a valid switch with a space and then some text, it opens the script in interactive mode, which I do not want. Is there a way to block additional arguments beyond the valid set?
This is where I check if the script is run interactively (double-click):
Code: Select all
if "%~1"=="" (
cls
call :env
goto :menu
)
I'm then checking for valid arguments:
Code: Select all
if "!switch!" equ "/p" (
cls
call :printer
) else if "!switch!" equ "/o" (
cls
call :os
) else if "!switch!" equ "/q" (
cls
call :general
) else (
cls
echo Please enter a valid switch. Type info.bat /? for a list of available switches.
exit /b
)
In this case, typing "info.bat /p 5" will open the script interactively. Thanks in advance for any suggestions you might have.
Re: Ignoring Arguments
Posted: 26 Aug 2021 22:17
by T3RRY
One option is to use a for loop with a list of valid switches to set a flag variable true if the current arg is a valid switch, then assign the next parameter as the switch value.
Switch values should always be doublequoted to guard against posion characters and to ensure whole strings containing standard delims are correctly captured as the switch value.
The alternative would be to use a series of substring modifications to identify the start of the next switch in the arg string and seperate the switch value, however there are more issues with characters that are not handled by substring modification then there is by using shift with a loop.
Note: the use of Call to forward the arg string doubles any Carets in switch values.
an example:
Code: Select all
@Echo off
Setlocal EnableExtensions
Call :GetArgs %*
If not errorlevel 1 Echo(Your usage info here
Set Switch[ 2> nul
Endlocal & Goto :eof
=============================================
:GetArgs REM define valid_Switches list below
Set valid_Switches="/a" "/b" "/c" "/f" "-a" "-b" "-c" "-f"
If "%~1"=="" Exit /B 0
For /f "tokens=1 Delims==" %%G in ('Set "Switch[" 2^> nul')Do Set "%%~G="
Set "?Switch{I}=0"
:processSwitches
If "%~1"=="" Exit /B %?Switch{I}%
Set "?Switch_Name=%~1"
Set "?Switch_Valid="
Set "?Switch_NoSubArg="
For %%G in (%valid_Switches%)Do If "%%~G"=="%~1" (
Call :Verify "%~2"
If not errorlevel 1 (
Set "?Switch_Valid=t"
Set /A "?Switch{I}+=1"
)Else (
Set "?Switch_NoSubarg=t"
Set /A "?Switch{I}+=1"
)
)
If Defined ?Switch_Valid Set "Switch[%?Switch_Name:~1%]=%~2"
If Defined ?Switch_NoSubarg Set "Switch[%?Switch_Name:~1%]=true"
Shift
Goto :processSwitches
:Verify
%= Handle last switch with no Subarg =%
If "%~1"=="" Exit /b 1
%= Handle Switch with no Subarg followed by another Switch =%
For %%V in (%valid_Switches%)Do If "%%~V"=="%~1" Exit /b 1
%= Flag Switch as trailed by Subarg =%
Exit /b 0
To handle Args as well as switches with / without subargs:
Code: Select all
@Echo off & Cls
Setlocal EnableExtensions
Call :GetArgs %*
If not errorlevel 1 (
Echo(No Valid Switches detected.
Echo(%Valid_Switches:"=%
)
Set Switch[ 2> nul
Set Arg[ 2> nul
Endlocal & Goto :eof
======================================
:GetArgs <%*>
%= Example define valid_Switches list below =%
Set valid_Switches="/a" "/b" "/c" "/f" "-a" "-b" "-c" "-f"
If "%~1"=="" Exit /B 0
For /f "tokens=1 Delims==" %%G in ('Set "Switch[" 2^> nul')Do Set "%%~G="
For /f "tokens=1 Delims==" %%G in ('Set "Arg[" 2^> nul')Do Set "%%~G="
Set "?Switch{I}=0"
Set "?Arg=0"
Set "?Switch_Encountered="
:processSwitches
If "%~1"=="" Exit /B %?Switch{I}%
Set "?Switch_Name=%~1"
Set "?Switch_Valid="
Set "?Switch_NoSubArg="
For %%G in (%valid_Switches%)Do If "%%~G"=="%~1" (
Set "?Switch_Encountered=True"
Call :Verify "%~2"
If not errorlevel 1 (
Set "?Switch_Valid=t"
Set /A "?Switch{I}+=1"
)Else (
Set "?Switch_NoSubarg=t"
Set /A "?Switch{I}+=1"
)
)
%= Define Switch[letter] with subarg value =%
If Defined ?Switch_Valid Set "Switch[%?Switch_Name:~1%]=%~2"
%= Define Switch[letter] as true - used with no subarg =%
If Defined ?Switch_NoSubarg Set "Switch[%?Switch_Name:~1%]=true"
%= Capture args preceeding switches as array =%
If not defined ?Switch_Encountered (
Set /A "?Arg{I}+=1"
For /f "Tokens=2 delims==" %%v in ('Set "?Arg{I}"')Do Set "Arg[%%v]=%~1"
)
Shift
Goto :processSwitches
:Verify
%= Handle last switch with no Subarg =%
If "%~1"=="" Exit /b 1
%= Handle Switch with no Subarg followed by another Switch =%
For %%V in (%valid_Switches%)Do If "%%~V"=="%~1" Exit /b 1
%= Flag Subvalue as invalid Switch =%
<nul Set /P "=%~1"|%__APPDIR__%Findstr.exe /ri "^[/-][abcdefghijklmnopqrstuvwxyz]*" > nul 2> nul && Exit /b 1
%= Flag Switch as trailed by Subarg =%
Exit /b 0
Re: Ignoring Arguments
Posted: 27 Aug 2021 06:06
by atfon
Hi T3RRY. Thank you for your feedback and suggested code. I will study it up and see how I can incorporate the elements I need.
Re: Ignoring Arguments
Posted: 27 Aug 2021 06:20
by T3RRY
atfon wrote: ↑27 Aug 2021 06:06
Hi T3RRY. Thank you for your feedback and suggested code. I will study it up and see how I can incorporate the elements I need.
As a function, its a simple addition. Switches used are returned to an array Switch[letter] with letters being sourced from the defined valid_Switches variable
Note: Valid switches need not be single letters, however should not contain standard delimiters.
If for example the file is called with the parameters: /a -c /f 7
The following definitions willl occur
Switch[a]=true
Switch[c]=true
Switch[f]=7
Simply assess the value of the relevant switch after calling :GetArgs to perform the conditional executions you desire.
The second version also builds an array of any args that preceede switch usage and explicitly precludes the possibility of an invalid Switch IE: /False from
being defined as the subarg value to a valid switch.
Re: Ignoring Arguments
Posted: 27 Aug 2021 06:33
by atfon
As a function, its a simple addition.
This may be true, but I'm no expert at batch scripting and I like to study the code so it meets my needs. For example, I may decide I want to limit switches to only one. Plus, I still want the file to launch with the name of the batch alone or by double-clicking in interactive mode.
Anyhow, that you again for your time and effort.
Re: Ignoring Arguments
Posted: 27 Aug 2021 06:53
by T3RRY
atfon wrote: ↑27 Aug 2021 06:33
For example, I may decide I want to limit switches to only one.
I'm sure the following is self explanatory with regards to how to maintain or modify what switches you wish to accept
Code: Select all
%= Example define valid_Switches list below =%
Set valid_Switches="/a" "/b" "/c" "/f" "-a" "-b" "-c" "-f"
And a count of Switches used is returned in the variable: ?Switch{I}
atfon wrote: ↑27 Aug 2021 06:33
Plus, I still want the file to launch with the name of the batch alone
In the second version, args that precede switches [even if no switches are used] are defined to an arg array, the count of which is returned in: ?Arg{I}
atfon wrote: ↑27 Aug 2021 06:33
or by double-clicking in interactive mode.
The assement following the call to :GetArgs in the above examples is for example purposes only. getArgs returns silently regardless of arg / switch usage.
the value of the variables ?Switch{I} and ?Arg{I} are available for assement to determine how many, if any, arguments or switches the script was called with, prior to you assessing the values of those Args or Switches.
Re: Ignoring Arguments
Posted: 27 Aug 2021 07:07
by atfon
I'm sure the following is self explanatory with regards to how to maintain or modify what switches you wish to accept
I apologize. I misspoke. I meant I may limit how many switches I wish to accept, not
which switches I wish to accept. I'm reviewing your script and some other script I have found on this forum which utilize argument switches.
Re: Ignoring Arguments
Posted: 27 Aug 2021 07:52
by T3RRY
atfon wrote: ↑27 Aug 2021 07:07
I'm sure the following is self explanatory with regards to how to maintain or modify what switches you wish to accept
I apologize. I misspoke. I meant I may limit how many switches I wish to accept, not
which switches I wish to accept. I'm reviewing your script and some other script I have found on this forum which utilize argument switches.
If you want to Exclude subsequent switches after one is defined, I'm sure you'll see where you need to add an "exit /b 1"
Alternately, you could modify the definition of the Switch[index] array to use the ?Switch{I} count instead of the Switch letter and just use the value of Switch[1]
I can't guess the order of priority you wish to place on switch assessment [ If you have multiple permitted switches but only want to accept one, how do you decide which to keep? ]
The list form of valid_Switches makes it easy for you to implement priority by defining the Valid_Switches variable in the order of importance, regardless of which of the two exclusion approaches described above you choose.
Theres also the choice of modifying the switch array definition to support both count and letter indexing:
Code: Select all
@Echo off
%= Example. define valid_Switches variable for your script as doublequoted list below =%
Set valid_Switches="/a" "/b" "/col" "/f" "-a" "-b" "-col" "-f"
Setlocal EnableExtensions
Call :GetArgs %*
If %Errorlevel% EQU 0 (
%= To notify usage in event of no args or switches remove rem below =%
Rem For %%G in (%Valid_Switches:" "=|%)Do Echo(No paramaters. %~n0 [Args] [%%~G]
)
Set Switch[ 2> nul
Set Arg[ 2> nul
Pause
Endlocal & Goto :eof
======================================
:GetArgs <%*>
:: Return values:
:: Errorlevel 0 No Args, No Switches
:: ?Arg{I} Count of Arguments
:: ?Switch{I} Count of Switches used
:: Arg[i] Index Argument value at position i
:: Switch.i Index Switch value at position i
:: Switch[string] Index value of /String or -String defined in valid_Switches [true if used without subarg; else subargs value]
If not defined Valid_Switches (
Echo(Define Valid_Switches Variable prior to calling %~n0
Pause
Exit
)
If "%~1"=="" Exit /B 0
For /f "tokens=1 Delims==" %%G in ('Set "Switch" 2^> nul')Do Set "%%~G="
For /f "tokens=1 Delims==" %%G in ('Set "Arg[" 2^> nul')Do Set "%%~G="
Set "?Switch{I}=0"
Set "?Arg{I}=0"
Set "?Switch_Encountered="
:processParams
If "%~1"=="" Exit /B 1
Set "?Switch_Name=%~1"
Set "?Switch_Valid="
Set "?Switch_NoSubArg="
For %%G in (%valid_Switches%)Do If "%%~G"=="%~1" (
Set "?Switch_Encountered=true"
Call :Verify "%~2"
If not errorlevel 1 (
Set "?Switch_Valid=t"
Set /A "?Switch{I}+=1"
)Else (
Set "?Switch_NoSubarg=t"
Set /A "?Switch{I}+=1"
)
)
%= Define Switch[letter] with subarg value =%
If Defined ?Switch_Valid (
Set "Switch[%?Switch_Name:~1%]=%~2"
For /f "Tokens=2 delims==" %%v in ('Set "?Switch{I}"')Do Set "Switch.%%v=%~2"
)
%= Define Switch[letter] as true - used with no subarg =%
If Defined ?Switch_NoSubarg (
Set "Switch[%?Switch_Name:~1%]=true"
For /f "Tokens=2 delims==" %%v in ('Set "?Switch{I}"')Do Set "Switch.%%v=true"
)
%= Capture args preceeding switches as array =%
If not defined ?Switch_Encountered (
%= Exclude invalid switch pattern =%
Call :Verify "%~1"
If not Errorlevel 1 (
Set /A "?Arg{I}+=1"
For /f "Tokens=2 delims==" %%v in ('Set "?Arg{I}"')Do Set "Arg[%%v]=%~1"
)
)
Shift
Goto :processParams
:Verify
%= Handle last switch with no Subarg =%
If "%~1"=="" Exit /b 1
%= Handle Switch with no Subarg followed by another Switch =%
For %%V in (%valid_Switches%)Do If "%%~V"=="%~1" Exit /b 1
%= Flag Subvalue as invalid Switch if SINGLE LETTER =%
<nul Set /P "=%~1"|%__APPDIR__%Findstr.exe /ri "^[/-][abcdefghijklmnopqrstuvwxyz]$" > nul 2> nul && (
%= Terminate Arg array definition on encountering invalid switch =%
Set "?Switch_Encountered=true"
Exit /b 1
)
%= Flag Switch as trailed by Subarg =%
Exit /b 0
Re: Ignoring Arguments
Posted: 30 Aug 2021 07:27
by atfon
Since I am currently only going to accept a single argument, I ended up using a less robust solution than T3RRY suggested. If at a later time I decide to allow for additional arguments, I will revisit this topic again. Once more, thank you T3RRY for all your helpful suggestions. This is an abbreviated version what I ended up using. I'm sure there are plenty of holes in it, but it seems to meet my needs for now:
Code: Select all
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
cls
call :env
goto :banner
)
if .%2 equ . (
set "switch=%~1"
if "!switch:~0,1!" equ "-" set "switch=/!switch:~1!"
if "!switch:~0,1!" neq "/" (
cls
echo Please enter a valid switch.
exit /b
)
if "!switch!" equ "/p" (
cls
call :printer
) else if "!switch!" equ "/o" (
cls
call :os
) else if "!switch!" equ "/q" (
cls
call :general
) else (
cls
echo Please enter a valid switch.
exit /b
)
)
if .%3 geq . (
cls
echo Please enter only one valid switch.
exit /b
)
Re: Ignoring Arguments
Posted: 30 Aug 2021 11:28
by Squashman
This topic may be of interest to you as well.
foolproof counting of arguments
Re: Ignoring Arguments
Posted: 30 Aug 2021 11:41
by atfon
Thanks, Squashman! That is an interesting discussion.
**Slightly off topic. Do you folks have a reliable method you use to search this forum? I find the phpBB forum search features could be better.***
Re: Ignoring Arguments
Posted: 30 Aug 2021 11:54
by Squashman
atfon wrote: ↑30 Aug 2021 11:41
**Slightly off topic. Do you folks have a reliable method you use to search this forum? I find the phpBB forum search features could be better.***
I always use Google.
arguments site:dostips.com
Re: Ignoring Arguments
Posted: 30 Aug 2021 12:15
by atfon