56

I need to be able to pass parameters to a windows batch file BY NAME (and NOT by order). My purpose here is to give end user the flexibility to pass parameters in any order, and the batch file should still be able to process them.

An example to make my question clearer:

in the command line, user does the following: somebatchfile.bat originalFile.txt newFile.txt

Inside somebatchfile.bat there is a simple statement to copy the contents of original file (first parameter %1%) to the new file (second parameter %2%). It could be as simple as the following statement: copy %1% %2%

Now, if user passes the above parameters in reverse order, the result will be far from desirable (very WRONG in fact).

So, is there a way for user to pass parameters by name: e.g. somebatchfile.bat "SOURC=originalFile.txt" "TARGET=newFile.txt" and for script to recognize them and use'em in correct places e.g. copy %SOURCE% %TARGET%?

Thanks,

Pouya
  • 563
  • 1
  • 4
  • 4

9 Answers9

36

Yeah you could do something like that though I don't think you can use "=" as a token delimiter. You could use say a colon ":", somebatchfile.bat "SOURC:originalFile.txt" "TARGET:newFile.txt". Here is an example of how you might split the tokens:

@echo off

set foo=%1
echo input: %foo%

for /f "tokens=1,2 delims=:" %%a in ("%foo%") do set name=%%a & set val=%%b

echo name:  %name%
echo value: %val%

Running this would produce this:

C:\>test.bat SOURC:originalFile.txt
input: SOURC:originalFile.txt
name:  SOURC
value: originalFile.txt

[Edit]

Ok, maybe it was too close to bed time for me last night but looking again this morning, you can do this:

@echo off

set %1
set %2

echo source: %SOURCE%
echo target: %TARGET%

Which would produce this (note that I reversed the source and target on the command line to show they are set and retrieved correctly):

C:\>test.bat "TARGET=newFile.txt" "SOURCE=originalFile.txt"
source: originalFile.txt
target: newFile.txt

Note that %1 and %2 are evaluated before the set so these do get set as environment variables. They must however be quoted on the command line.

Dave Rager
  • 8,002
  • 3
  • 33
  • 52
  • thanks Dave, but your solution involves "quite a bit of processing" inside of the batch file.however, what's desirable is a "built-in" functionality sort of thing that automatically populates internal variables (in user's script) with parameters passed. for example: by convention, they know that if they pass a parameter like this: "SOURC:originalFile.txt" then their internal variables %name% and %val% will automatically get the right values. – Pouya Mar 07 '11 at 05:58
  • 6
    @Pouya: This **batch script** parses the pair of `name & value` with just one line of code. How can this be *quite a bit of processing*? But then, I see that you are talking about having a built-in functionality – well, no, batch scripting is quite a limited tool, which Microsoft has (once more) indirectly admitted by introducing PowerShell. – Andriy M Mar 07 '11 at 08:22
  • Andriy, problem is: in our case, all of the batch files are provided by end users, and normally you don't want to ask end users to add even one single line of code to their existing scripts just to make'em compatible with your solution. – Pouya Mar 07 '11 at 10:38
  • " "quite a bit of processing" " -- Huh? This solution seems elegant, effective, and succinct. – johny why Nov 20 '21 at 01:03
  • @Pouya " you don't want to ask end users to add even one single line of code to their existing scripts" -- Huh? I thought YOU'RE the person making a script here, not users. You're saying you want users to pass by name, AND you want users to keep their existing scripts. What? So are their existing scripts passing by name or not? (i know this is an ancient thread, but what the hey. Sometimes people respond :D – johny why Nov 20 '21 at 01:10
13

A bit late to the party :) This is my suggestion for managing "posix like" options. For example mybatchscript.bat -foo=foovalue -bar=barvalue -flag

:parseArgs
:: asks for the -foo argument and store the value in the variable FOO
call:getArgWithValue "-foo" "FOO" "%~1" "%~2" && shift && shift && goto :parseArgs

:: asks for the -bar argument and store the value in the variable BAR
call:getArgWithValue "-bar" "BAR" "%~1" "%~2" && shift && shift && goto :parseArgs

:: asks for the -flag argument. If exist set the variable FLAG to "TRUE"
call:getArgFlag "-flag" "FLAG" "%~1" && shift && goto :parseArgs


:: your code here ...
echo FOO: %FOO%
echo BAR: %BAR%
echo FLAG: %FLAG%

goto:eof

..and here the functions that do the job. You should put them in the same batch file

:: =====================================================================
:: This function sets a variable from a cli arg with value
:: 1 cli argument name
:: 2 variable name
:: 3 current Argument Name
:: 4 current Argument Value
:getArgWithValue
if "%~3"=="%~1" (
  if "%~4"=="" (
    REM unset the variable if value is not provided
    set "%~2="
    exit /B 1
  )
  set "%~2=%~4"
  exit /B 0
)
exit /B 1
goto:eof



:: =====================================================================
:: This function sets a variable to value "TRUE" from a cli "flag" argument
:: 1 cli argument name
:: 2 variable name
:: 3 current Argument Name
:getArgFlag
if "%~3"=="%~1" (
  set "%~2=TRUE"
  exit /B 0
)
exit /B 1
goto:eof

Save the file as mybatchscript.bat and run

mybatchscript.bat -bar=barvalue -foo=foovalue -flag

You'll get:

FOO: foovalue
BAR: barvalue
FLAG: TRUE
Federico Destefanis
  • 968
  • 1
  • 16
  • 27
  • this not works if you have space in your parameter value even in double quotation like this: -dirName="Network Software" – Saeid Ostad Dec 05 '18 at 19:04
12

Other way I quite liked:

set c=defaultC
set s=defaultS
set u=defaultU

:initial
if "%1"=="" goto done
echo              %1
set aux=%1
if "%aux:~0,1%"=="-" (
   set nome=%aux:~1,250%
) else (
   set "%nome%=%1"
   set nome=
)
shift
goto initial
:done

echo %c%
echo %s%
echo %u%

Run the following command:

arguments.bat -c users -u products

Will generate the following output:

users
defaultS
products
  • it does not work with parameters that contain spaces, how it could be improved to support that case? – m0dest0 Aug 18 '17 at 04:16
  • 2
    arguments with spaces should be in double quotes – iMatoria Apr 08 '18 at 12:11
  • One minor addition after `set nome=%aux:~1,250%`: `if not defined !name! (echo oops! )`. It won't stop you setting unrelated environment variables, but it should help to catch typos. – Tony Graham Feb 28 '20 at 16:48
7

I wanted to see the possibility of reading named parameter supplied to a batch program. For example :

myBatch.bat arg1  arg2 

Reading parameter can be done as follows :

%~1 would be arg1
%~2 would be arg2

Above argument supplied to batch program is easy to read but then each time you execute it, you have to maintain order and remember what arg1 is supposed to have. In order to overcome it, parameter can be supplied as key:value format. And command would look like below :

mybatch.bar key1:value1 key2:value2

Though there is no straight forward way to parse such argument list. I wrote following nested for loop in batch script which will basically parse and create environment variable with key1 as name, and value1 assigned to it. This way you can read all argument supplied using straight forward way of reading environment variable.

@echo off
FOR %%A IN (%*) DO (
   FOR /f "tokens=1,2 delims=:" %%G IN ("%%A") DO setLocal %%G=%%H
)

Afterwards, you can use %key1% format to read all argument being supplied.

HTH

Gaurav Daga
  • 343
  • 2
  • 11
  • setLocal usually works for within the scope of batch program, I am not sure what scope you were attempting to set these in. I prefer local scope considering these parameters will be ( and should be) used within batch scope. How were you trying to read it? Was there a EndLocal issued before you attempted to read these parameters? – Gaurav Daga Dec 22 '14 at 18:36
  • I used [IzPack's executable](http://izpack.org/documentation/installation-files.html#executable-mark-file-executable-or-execute-it) tag to pass arguments to my script. I just wrote these arguments to file: `echo INSTALL_PATH: %INSTALL_PATH% >> text.txt`. – Matthias Braun Dec 22 '14 at 19:48
7

Very old question, but I think I found a neat way (with no external dependencies) of passing key=value arguments to a batch file. You can test the method with the following MWE:

@echo off
chcp 65001

rem name this file test.bat and call it with something like:
rem test keyc=foo keyd=bar whatever keya=123 keyf=zzz

setlocal enabledelayedexpansion

set "map=%*"
call :gettoken keya var1
call :gettoken keyb var2
call :gettoken keyc var3

rem replace the following block with your real batch
rem ************************************************
echo:
echo Map is "%map%"
echo Variable var1 is "%var1%"
echo Variable var2 is "%var2%"
echo Variable var3 is "%var3%"
echo:
echo Done.
echo:
pause
rem ************************************************

goto :eof
:gettoken
call set "tmpvar=%%map:*%1=%%"
if "%tmpvar%"=="%map%" (set "%~2=") else (
for /f "tokens=1 delims= " %%a in ("%tmpvar%") do set tmpvar=%%a
set "%~2=!tmpvar:~1!"
)
exit /b

The method is quite:

  • flexible, because key=value arguments can be mixed with other arguments (such as filenames), and because the name of the variable used to store the value is independent from the name of key
  • scalable, because it's easy to add keys, just add a new call :gettoken ... line
  • robust, because anything passed that is not allowed by defined keys is ignored, and unassigned keys get an empty value (if you want you can easily change this behavior so they get something like null or undefined instead of empty)

A drawback is that it get confused if a key has a name that is part of another key name. So, for example, if you have a key like width, don't define a key like newwidth.

Enjoy.

mmj
  • 5,514
  • 2
  • 44
  • 51
6

Take this attempt (pure batch):

set PARAM_0=0
:parameters_parse

set parameter=%~1
if "%parameter%"=="" goto parameters_parse_done
if "%parameter:~0,1%"=="-" (
    set ARG_%parameter:~1%="%~2"
    shift
    shift
    goto parameters_parse
)
if "%parameter:~0,1%"=="/" (
    set ARG_%parameter:~1%="%~2"
    shift
    shift
    goto parameters_parse
)
set /a PARAM_0=%PARAM_0%+1
set PARAM_%PARAM_0%="%~1"
shift
goto parameters_parse

:parameters_parse_done

rem Insert your script here

and some tests:

call args.bat /bar="Hello World" Test1 -baz "Test test test" /foo=Test1 Test2

echo foo=%ARG_foo%
echo bar=%ARG_bar%
echo baz=%ARG_baz%
echo count=%PARAM_0%
echo 1=%PARAM_1%
echo 2=%PARAM_2%

Outputs:

foo="Test1"
bar="Hello World"
baz="Test test test"
count=2
1="Test1"
2="Test2"
PiotrK
  • 4,210
  • 6
  • 45
  • 65
4

My gosh people; You're overthinking this! :)

OP's CMD args are all in the form "[VarName]=[Something]"

So the 1st line could simply be FOR %%_ IN (%*) DO SET "%%~_"

or in nicer looking code

FOR %%_ IN (%*) DO SET "%%~_"

That said here is a more complete script example where you could just put all of your script within the :Main function

@( Setlocal
  ECHO OFF
  FOR %%_ IN (%*) DO SET "%%~_"
)

CALL :Main

( ENDLOCAL
  EXIT /B )

:Main
  REM Your code goes here.
  REM Your code goes here.
  REM Your code goes here.
  REM Your code goes here.
GOTO :EOF
Ben Personick
  • 3,074
  • 1
  • 22
  • 29
2

I think you are looking for getopt kind of support in Windows batch scripts which unfortunately doesn't exist in entirety. The closest you can probably get is using GetOpt.btm script. With this then you can execute your script using command:

somebatchfile.bat /SOURC:originalFile.txt /TARGET:newFile.txt

Code:

@echo off
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: GetOpt - Process command line options
:: 
:: Michael Fross
:: [email]michael@fross.org[/email]
:: [url]http://fross.org[/url]
::
:: 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 ':'
::
:: Use "If defined OPTION_arg" or "If %OPTION_arg eq value" to test for options
::
:: It also sets a parameter variable for each paramater entered: PARAM_1 to PARAM_n
:: PARAM_0 is a special value that contains the number of PARAMs.  Useful for looping
:: through all of them.  For example, do i = 1 to %PARAM_0 by 1 ...
::
:: In your batch file call getopt as:
::      call GetOpt.btm %$
::
:: I also recommend setting setlocal and endlocal in the host batch file so that
:: the option / param variable do not stick around after the host batch files exits.
::
:: Example usage:  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
::
:: To get debug messages, set DEBUG=1.  This will give detailed information for each
:: parameter on the command line as getopt loops through the list.
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: Clean up the environment before we get going
unset getopt* OPTION_* PARAM_*
set getopt_ParmCounter=1

:: If in debug mode, kick off the display by showing the number of arguments
if defined DEBUG echo GetOpt is processing %# arguments:

:: Loop through all command line arguments one at a time.
for /L %i in (1,1,%#) do (
   if defined DEBUG (echo. %+ echo Scan #%i:)

   :: If first character starts with a - or / it must be an option
   iff %@instr[0,1,%[%i]] == - .or. %@instr[0,1,%[%i]] == / then
      set getopt_Parm=%[%i]
      if defined DEBUG echo  - Item "%getopt_Parm" is an option.

      :: Set the Equal Index to the position of the colon.  0 means none was found
      set getopt_EqIdx=%@index[%getopt_Parm,:]

      :: Display the index position of the colon
      if defined DEBUG .AND. %getopt_EqIdx GE 0 echo  - Found colon at index position "%getopt_EqIdx"

      :: If the index is GE 0 then we must have a colon in the option.
      :: set the OPTION value to the stuff to the right of the colon
      iff %getopt_EqIdx ge 0 then
         set getopt_ParmName=%@instr[2, %@Dec[%getopt_EqIdx] , %getopt_Parm]
         if defined DEBUG echo  - ParmName  = "%getopt_ParmName"
         set getopt_ParmValue=%@right[%@eval[-%getopt_EqIdx-1],%getopt_Parm]
         if defined DEBUG echo  - Parmvalue = "%getopt_ParmValue"
         set OPTION_%getopt_ParmName=%getopt_ParmValue
      else
         :: This is a flag, so simply set the value to 1
         if defined DEBUG echo  - No colon found in "%getopt_Parm"
         set getopt_ParmName=%@right[%@Dec[%@len[%getopt_Parm]],%getopt_Parm]
         set getopt_ParmValue=1
         if defined DEBUG echo  - ParmName = "%getopt_ParmName"
         set OPTION_%getopt_ParmName=%getopt_ParmValue
      endiff

      :: Regardless if there was a value or not, display what is going to occur
      if defined DEBUG echo  - Setting Variable OPTION_%getopt_ParmName=%getopt_ParmValue
   else
      :: There was no / or - found, therefore this must be a paramater, not an option
      if defined DEBUG echo  - "%[%i]" is a parameter, not an option
      set PARAM_%getopt_ParmCounter=%[%i]
      set PARAM_0=%getopt_ParmCounter
      if defined DEBUG echo  - Updating Number of Parms.  PARAM_0=%PARAM_0
      if defined DEBUG echo  - Setting Variable PARAM_%getopt_ParmCounter = %[%i]
      set getopt_ParmCounter=%@Inc[%getopt_ParmCounter]
   endiff
)

:: Display additional information
iff defined DEBUG then
   echo.
   echo There were %PARAM_0 parameters found.  Setting PARAM_0=%PARAM_0
   echo.
   echo GetOpt has completed processing %# arguments.  Ending Execution.
endiff

:: Perform cleanup
unset getopt_*
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
anubhava
  • 761,203
  • 64
  • 569
  • 643
1
:parse
IF "%~1"=="" GOTO endparse
ECHO "%~1"| FIND /I "=" && SET "%~1"
SHIFT /1
GOTO parse
:endparse

This code checks all the parameters, strips all the outer quotes, and if equal sign is present, sets variable to value.

Use it like this:

yourbatch.bat "foo=bar" "foo2=bar2"

based on this answer: Using parameters in batch files at DOS command line, and edited in response to the edit attempt by D.Barev

robotik
  • 1,837
  • 1
  • 20
  • 26
  • This would only work for quoted parameters as `=` is treated as a separator by CMD.exe if not within quotes. – robotik Feb 18 '20 at 14:48