Is there a way to parse arguments into a batch script

Posted on

QUESTION :

I have found a very useful script here that will parse in arguments to a batch file and process them as follows:

BatchFile.btm /a /b:22 /longopt Parm1 Parm2 /quotedArg:"long quoted arg"
   - OPTION_a will equal 1.
   - OPTION_b will equal 22
   - OPTION_quotedArg will equal "long quoted arg"
   - OPTION_longopt will eqal 1.
   - PARAM_1 will equal Parm1
   - PARAM_2 will equal Parm2
   - PARAM_0 will be set to the number of parms, so 2 in this case

However, this script is written for .btm files and doesn’t seem to be suitable for the DOS emulator or more recent versions of Windows. Can anyone translate this or know where to find an up-to-date equivalent that will work in DOS emulator in Win7/Svr2003?

ANSWER :

I have managed to translate the majority of this script to work in a Windows 7 batch file. I have not tested this against any other version of Windows.

This program scans the command line sent to it and sets various
environment variables that coorespond to the settings.

It sets an OPTION_arg variable for each arg on the command line.
If a switch, the env var is set to 1. If a value is given via the colon sign,
it’s set to that value. Note, there can not be any white space around the :
[Modification] This modified script also cannot handle option or parameter values containing spaces, even when encased in parenthesis. [/Modification]

Use If defined OPTION_arg or if /i "%OPTION_arg%"=="value" to test for options

It also sets a parameter variable for each paramater entered: PARAM_1 to PARAM_n and
PARAM_0 is a special value that contains the number of PARAMs. Useful for looping
through all of them. For example for /l (1,1,%PARAM_0%) do ...

In your batch file call getopt as call GetOpt.bat %*

I also recommend setting setlocal and endlocal in the host batch file so that
the option and param variables do not stick around after the host batch files exits.

Example usage:  HostBatchFile.bat /a /b:22 /longopt Parm1 Parm2
   OPTION_a will equal 1.
   OPTION_b will equal 22
   OPTION_longopt will eqal 1.
   PARAM_1 will equal Parm1
   PARAM_2 will equal Parm2
   PARAM_0 will be set to the number of parms, so 2 in this case

The parts I have not translated are:

  1. Incorporating any kind of DEBUG flag for screen output
  2. Being able to handle “strings with spaces” for either options or parameters as the initial separation of arguments fed in are split using <SPACE>

Here is the translated script.

@echo off
cls

set getopt_ParmCounter=1
set paramc=1
set DEBUG=1

set argc=0
for %%x in (%*) do Set /A argc+=1
echo Number of arguments: %argc%
echo %*&echo.

set _myvar=%*

rem Loop through all command line arguments one at a time
:varloop
set isparam=1
for /f "tokens=1*" %%a in ('echo %_myvar%') DO (
   set getopt_Parm=%%a
   set _myvar=%%b
   call :paramtype

   rem shift along arguments and rerun loop
   if NOT "%%b"=="" goto varloop
)
goto :eof

:paramtype
rem If first character starts with a - or / it must be an option
if /i "%getopt_Parm:~0,1%"=="-" call :option
if /i "%getopt_Parm:~0,1%"=="/" call :option 
if /i "%isparam%"=="1" call :param
goto :eof

:option
   set isparam=0
   rem Set the Equal Index to the position of the colon.  0 means none was found
   for /f %%j in ('findstring %getopt_Parm% :') do set getopt_EqIdx=%%j

   rem If the index is GE 0 then we must have a colon in the option.
   if /i "%getopt_EqIdx%"=="0" (call :nocolon) else (call :colon)
   goto :eof

      :colon
         rem set the OPTION value to the stuff to the right of the colon
         set /a getopt_ParmNameEnd=%getopt_EqIdx%-2
         call set getopt_ParmName=%%getopt_Parm:~1,%getopt_ParmNameEnd%%%
         call set getopt_ParmValue=%%getopt_Parm:~%getopt_EqIdx%%%
         set OPTION_%getopt_ParmName%=%getopt_ParmValue%
         goto :eof

      :nocolon
         rem This is a flag, so simply set the value to 1
         set getopt_ParmName=%getopt_Parm:~1%
         set getopt_ParmValue=1
         set OPTION_%getopt_ParmName%=%getopt_ParmValue%
         goto :eof

:param
   rem There was no / or - found, therefore this must be a paramater, not an option
   set PARAM_%getopt_ParmCounter%=%getopt_Parm%
   set PARAM_0=%getopt_ParmCounter%
   set /a getopt_ParmCounter+=1
   goto :eof

%* for every parameter. You might also find these useful:

%0 – the command used to call the batch file (could be foo, ..foo, c:batsfoo, etc.)
%1 is the first command line parameter,
%2 is the second command line parameter,
and so on till %9 (and SHIFT can be used for those after the 9th).

%~nx0 – the actual name of the batch file, regardless of calling method (some-batch.bat)
%~dp0 – drive and path to the script (d:scripts)
%~dpnx0 – is the fully qualified path name of the script (d:scriptssome-batch.bat)

More info examples at http://www.ss64.com/nt/syntax-args.html and http://www.robvanderwoude.com/parameters.html

Source: https://stackoverflow.com/questions/357315/get-list-of-passed-arguments-in-windows-batch-script-bat

The batch scripting language does not support functions / procedures, so its modular design is hardly mentioned, but you can implement them with the help of its setlocal [enabledelayedexpansion] / endlocal. Under this mechanism, it is not too difficult to simulate “parameter” or “argument”, except for the return of arrays, especially the return of multiple arrays.
Here, I implemented the return of the array, including the return of multiple arrays. See the code below for details. When I searched for implementation “getopt” with batch, I saw the above code. The following code(/getopt.bat) is written based on the reference above code:

::
:://////////////////////////////////////////////////////////////////////////////
:: reference: https://superuser.com/questions/637133/is-there-a-way-to-parse-arguments-into-a-batch-script
::
@echo off


::
:://////////////////////////////////////////////////////////////////////////////
::
:getopt
    call :_getopt "%*" parmValueArr parmEndIdx optNameArr optValueArr optEndIdx
    set /a parmLastIdx=parmEndIdx-1 & set /a optLastIdx=optEndIdx-1
    set PARAM_0=!parmEndIdx!
    for /l %%a in (0,1,%parmLastIdx%) do (
        set /a "IdxAdd1=%%a+1"
        set PARAM_!IdxAdd1!=!parmValueArr[%%a]!
    )
    for /l %%a in (0,1,%optLastIdx%) do (
        set OPTION_NAME=OPTION_!optNameArr[%%a]!
        set !OPTION_NAME!=!optValueArr[%%a]!
    )
    goto :eof

::
:://////////////////////////////////////////////////////////////////////////////
::
:_getopt {%1 argvs_val[/in]} {%2 parmValueArr[/out]} {%3 parmEndIdx[/out]} {%4 optNameArr[/out]} {%5 optValueArr[/out]} {%6 optEndIdx[/out]}
    setlocal EnableDelayedExpansion
    set "parm2parse=%~1"
    set /a parmEndIdx=0
    set /a optEndIdx=0
    :__getopt_var_loop
    for /f "tokens=1*" %%a in ('echo %parm2parse%') do (
        set "getopt_parm=%%a"
        set "parm2parse=%%b"
        set "parmValue=" & set "optName=" & set "optValue="
        call :__getopt_param_type "!getopt_parm!" parmValue optName optValue
        if not "!parmValue!"=="" (
            set "%~2[!parmEndIdx!]=!parmValue!"
            set /a parmEndIdx+=1
        ) else (
            set "%~4[!optEndIdx!]=!optName!"
            set "%~5[!optEndIdx!]=!optValue!"
            set /a optEndIdx+=1
        )

        if NOT "!parm2parse!"=="" (
            goto :__getopt_var_loop
        )
    )
    set %~2[=MAKE_SURE_TO_ENTER_THE_LOOP
    set %~4[=MAKE_SURE_TO_ENTER_THE_LOOP
    set %~5[=MAKE_SURE_TO_ENTER_THE_LOOP
    set SubEnviron=1
    (
        for /F "tokens=1,2 delims==" %%a in ('set %~2[ 2^>nul') do (
            if defined SubEnviron (
                for /F "tokens=1,2 delims==" %%m in ('set %~4[ 2^>nul') do (
                    if defined SubEnviron (
                        for /F "tokens=1,2 delims==" %%w in ('set %~5[ 2^>nul') do (
                            if defined SubEnviron endlocal
                            set "%%w=%%x"
                        )
                    )
                    set "%%m=%%n"
                )
            )
            set "%%a=%%b"
        )
        set %~2[=
        set %~4[=
        set %~5[=
        if defined SubEnviron endlocal
        set "%~3=%parmEndIdx%"
        set "%~6=%optEndIdx%"
        endlocal & goto :eof
    )

::
:://////////////////////////////////////////////////////////////////////////////
::
:__getopt_param_type argv_val[/in] parmValue_var[/out] optName_var[/out] optValue_var[/out]
    setlocal EnableDelayedExpansion
    set "argv=%~1"
    set "first_char=%argv:~0,1%"
    set "isoption=false"
    if /i "%first_char%"=="-" set "isoption=true"
    if /i "%first_char%"=="/" set "isoption=true"
    if "%isoption%"=="true" (
        call :___getopt_option "%argv%" optName optValue
    ) else (
        call :___getopt_param "%argv%" parmValue
    )
    endlocal & set "%~2=%parmValue%" & set "%~3=%optName%" & set "%~4=%optValue%" & goto :eof

::
:://////////////////////////////////////////////////////////////////////////////
::
:___getopt_option opt[/in] optName[/out] optValue[/out]
    setlocal
    set "getopt_Parm=%~1"
    set "getopt_Parm=%getopt_Parm:~1%"
    for /f "tokens=1* delims=:" %%a in ("%getopt_Parm%") do (
        set "optName=%%a"
        set "optValue=1"
        if not "%%b"=="" (
            set "optValue=%%b"
        )
    )
    endlocal & set "%~2=%optName%" & set "%~3=%optValue%" & goto :eof

::
:://////////////////////////////////////////////////////////////////////////////
::
:___getopt_param parm[/in] parmValue[/out]
    setlocal
    endlocal & set "%~2=%~1" & goto :eof

::
:://////////////////////////////////////////////////////////////////////////////
::
:PrintArray [arrayName] [lowBound] [upBound]
    setlocal enabledelayedexpansion
    For /L %%C in (%2 1 %3) do echo ^<%1[%%C]=!%1[%%C]!^>
    endlocal & goto :eof

:://////////////////////////////// END /////////////////////////////////////////

Here are the test cases(/getopt_test.bat):

@echo off

::
:://////////////////////////////// GETOPT_TEST_CASE0 ///////////////////////////
::
setlocal EnableDelayedExpansion
call getopt.bat /a /b:22 /longopt Parm1 Parm2
if not       "%OPTION_a%"=="1"     ( echo assert failed 01 )
if not       "%OPTION_b%"=="22"    ( echo assert failed 02 )
if not "%OPTION_longopt%"=="1"     ( echo assert failed 03 )
if not        "%PARAM_0%"=="2"     ( echo assert failed 04 )
if not        "%PARAM_1%"=="Parm1" ( echo assert failed 05 )
if not        "%PARAM_2%"=="Parm2" ( echo assert failed 06 )
endlocal

::
:://////////////////////////////// GETOPT_TEST_CASE1 ///////////////////////////
::
setlocal EnableDelayedExpansion
call getopt.bat /a /b:22 /longopt
if not       "%OPTION_a%"=="1"  ( echo assert failed 11 )
if not       "%OPTION_b%"=="22" ( echo assert failed 12 )
if not "%OPTION_longopt%"=="1"  ( echo assert failed 13 )
endlocal

::
:://////////////////////////////// GETOPT_TEST_CASE2 ///////////////////////////
::
setlocal EnableDelayedExpansion
call getopt Parm1 Parm2
if not "%PARAM_0%"=="2"     ( echo assert failed 21 )
if not "%PARAM_1%"=="Parm1" ( echo assert failed 22 )
if not "%PARAM_2%"=="Parm2" ( echo assert failed 23 )
endlocal

echo on & goto :eof

Leave a Reply

Your email address will not be published. Required fields are marked *