60

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,

1
  • Not since the demise of Algol-60. Commented Jul 16, 2018 at 8:50

10 Answers 10

38

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.

Sign up to request clarification or add additional context in comments.

5 Comments

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: 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, 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.
" "quite a bit of processing" " -- Huh? This solution seems elegant, effective, and succinct.
@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
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 

1 Comment

this not works if you have space in your parameter value even in double quotation like this: -dirName="Network Software"
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 

3 Comments

it does not work with parameters that contain spaces, how it could be improved to support that case?
arguments with spaces should be in double quotes
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.
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

2 Comments

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?
I used IzPack's executable tag to pass arguments to my script. I just wrote these arguments to file: echo INSTALL_PATH: %INSTALL_PATH% >> text.txt.
7

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" 

1 Comment

This answer works perfectly and should absolutely be higher!
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.

Comments

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 

2 Comments

This gives the following error:The following usage of the path operator in batch-parameter substitution is invalid: %~_"
@Evan MJ ah, typo on the syntax, it needs to be "%%~_" thanks
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][email protected][/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_* 

1 Comment

This code requires not only a lot of work, but also a 3rd party version of the windows CLI
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

1 Comment

This would only work for quoted parameters as = is treated as a separator by CMD.exe if not within quotes.
1

Based upon the answer of Davidson Alencar and combined with the answer of Gaurav Daga: a solution without GOTO label jumps:

@ECHO OFF SETLOCAL ENABLEDELAYEDEXPANSION REM Here can go default values SET a= SET b= SET c= SET d=default_value SET long_parameter= FOR %%A IN (%*) DO ( SET "aux=%%~A" IF "!aux:~0,1!"=="-" ( SET nome=!aux:~1,250! ) ELSE ( SET "!nome!=!aux!" SET nome= ) ) ECHO a: %a% ECHO b: %b% ECHO c: %c% ECHO d: %d% ECHO long_parameter: %long_parameter% ECHO not_set_parameter: %not_set_parameter% ECHO First argument: %1 

Calling with e.g.:

my_script.cmd -a "my var" -b 123 -c arg_without_whitespace_dont_need_quotes -long_parameter "true" -not_set_parameter true

Out:

a: my var b: 123 c: arg_without_whitespace_dont_need_quotes d: default_value long_parameter: true not_set_parameter: true first argument: -a 

Improvements:

  • Performance! GOTO will scan for the label in every iteration down the script, and at the end it will start from the top again. For my script with 8 parameters, the execution time dropped for this part by 80 %
  • SHIFT will shift the argument, and they will "loose" their position. With this solution the first argument will stay in %1

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.