Batch line parser:
Here is a brief overview of phases in the batch file line parser:
Phase 0) Read Line:
Phase 1) Percent Expansion:
Command line arguments
Batch scripts support the concept of command line arguments wherein arguments can be passed to the batch file when invoked. The arguments can be called from the batch files through the variables %1, %2, %3, and so on.
Command line parser:
Works like the BatchLine-Parser, except:
Phase 1) Percent Expansion:
- No
%*
,%1
etc. argument expansion - If var is undefined, then
%var%
is left unchanged. - No special handling of
%%
. If var=content, then%%var%%
expands to%content%
.
Delimiters
Some characters in the command line are ignored by batch files, depending on the DOS version, whether they are “escaped” or not, and often depending on their location in the command line:
- commas (“,”) are replaced by spaces, unless they are part of a string in doublequotes
- semicolons (“;”) are replaced by spaces, unless they are part of a string in doublequotes
- “=” characters are sometimes replaced by spaces, not if they are part of a string in doublequotes
- the first forward slash (“/”) is replaced by a space only if it immediately follows the command, without a leading space
- multiple spaces are replaced by a single space, unless they are part of a string in doublequotes
- tabs are replaced by a single space
- leading spaces before the first command line argument are ignored
- trailing spaces after the last command line argument are trimmed
I know of several occasions where these seemingly useless “features” proved very handy.Keep in mind, though, that these “features” may vary with the operating systems used.
More on command line parsing can be found on the PATH and FOR (especially FOR’s interactive examples) pages.
Local vs global variables
In any programming language, there is an option to mark variables as having some sort of scope, i.e. the section of code on which they can be accessed. Normally, variable having a global scope can be accessed anywhere from a program whereas local scoped variables have a defined boundary in which they can be accessed.
DOS scripting also has a definition for locally and globally scoped variables. By default, variables are global to your entire command prompt session. Call the SETLOCAL command to make variables local to the scope of your script. After calling SETLOCAL, any variable assignments revert upon calling ENDLOCAL, calling EXIT, or when execution reaches the end of file (EOF) in your script. The following example shows the difference when local and global variables are set in the script.
More options in windows nt 4/2000/xp
Windows NT 4 introduced a set of new features for command line parameters:
Windows 2000 and XP add even more options.More information can be found at the page explaining NT’s CALL command.
To remove the leading space of %* included by NT 4 use the following commands:
SET commandline=%* IF NOT CMDEXTVERSION 2 SET commandline=%commandline:~1%
Output
The above command produces the following output.
13150 5
Parsing of integer values
There are many different contexts where cmd.exe parses integer values from strings, and the rules are inconsistent:
SET /A
IF
%var:~n,m%
(variable substring expansion)FOR /F "TOKENS=n"
FOR /F "SKIP=n"
FOR /L %%A in (n1 n2 n3)
EXIT [/B] n
Details for these rules may be found at Rules for how CMD.EXE parses numbers
Shift
The batch file’s limitation to handle parameters up to %9 only can be overcome by using SHIFT.Let us assume your batchfile is called with the command line parameters A B C D E F G H I J K.Now %1 equals A, %2 equals B, etcetera, until %9, which equals I.
However, does not equal J but A0; is interpreted as %1, immediately followed by a 0.Does that mean the rest of the parameters is lost?
Of course not.After your batch file handled its first parameter(s) it could SHIFT them (just insert a line with only the command SHIFT), resulting in %1 getting the value B, %2 getting the value C, etcetera, till %9, which now gets the value J.
Substitution of batch parameters
For parameters that represent file names the shell provides lots of functionality related to working with files that is not accessible in any other way. This functionality is accessed with constructs that begin with %~.
For example, to get the size of the file passed in as an argument use
Syntax
set /A variable-name=value
where,
variable-name is the name of the variable you want to set.
value is the value which needs to be set against the variable.
/A – This switch is used if the value needs to be numeric in nature.
The following example shows a simple way the set command can be used.
Validate command line arguments using goto
A tip by Oliver Schneider:
Windows bat file optional argument parsing
The selected answer works, but it could use some improvement.
My solution relies on the creation of an OPTIONS variable that defines all of the options and their defaults. OPTIONS is also used to test whether a supplied option is valid. A tremendous amount of code is saved by simply storing the option values in variables named the same as the option. The amount of code is constant regardless of how many options are defined; only the OPTIONS definition has to change.
EDIT– Also, the :loop code must change if the number of mandatory positional arguments changes. For example, often times all arguments are named, in which case you want to parse arguments beginning at position 1 instead of 3. So within the :loop, all 3 become 1, and 4 becomes 2.
@echo off
setlocal enableDelayedExpansion
:: Define the option names along with default values, using a <space>
:: delimiter between options. I'm using some generic option names, but
:: normally each option would have a meaningful name.
::
:: Each option has the format -name:[default]
::
:: The option names are NOT case sensitive.
::
:: Options that have a default value expect the subsequent command line
:: argument to contain the value. If the option is not provided then the
:: option is set to the default. If the default contains spaces, contains
:: special characters, or starts with a colon, then it should be enclosed
:: within double quotes. The default can be undefined by specifying the
:: default as empty quotes "".
:: NOTE - defaults cannot contain * or ? with this solution.
::
:: Options that are specified without any default value are simply flags
:: that are either defined or undefined. All flags start out undefined by
:: default and become defined if the option is supplied.
::
:: The order of the definitions is not important.
::
set "options=-username:/ -option2:"" -option3:"three word default" -flag1: -flag2:"
:: Set the default option values
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop
:: Validate and store the options, one at a time, using a loop.
:: Options start at arg 3 in this example. Each SHIFT is done starting at
:: the first option so required args are preserved.
::
if not "%~3"=="" (
set "test=!options:*%~3:=! "
if "!test!"=="!options! " (
rem No substitution was made so this is an invalid option.
rem Error handling goes here.
rem I will simply echo an error message.
echo Error: Invalid option %~3
) else if "!test:~0,1!"==" " (
rem Set the flag option using the option name.
rem The value doesn't matter, it just needs to be defined.
set "%~3=1"
) else (
rem Set the option value using the option as the name.
rem and the next arg as the value
set "%~3=%~4"
shift /3
)
shift /3
goto :loop
)
:: Now all supplied options are stored in variables whose names are the
:: option names. Missing options have the default value, or are undefined if
:: there is no default.
:: The required args are still available in %1 and %2 (and %0 is also preserved)
:: For this example I will simply echo all the option values,
:: assuming any variable starting with - is an option.
::
set -
:: To get the value of a single parameter, just remember to include the `-`
echo The value of -username is: !-username!
There really isn’t that much code. Most of the code above is comments. Here is the exact same code, without the comments.
@echo off
setlocal enableDelayedExpansion
set "options=-username:/ -option2:"" -option3:"three word default" -flag1: -flag2:"
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop
if not "%~3"=="" (
set "test=!options:*%~3:=! "
if "!test!"=="!options! " (
echo Error: Invalid option %~3
) else if "!test:~0,1!"==" " (
set "%~3=1"
) else (
set "%~3=%~4"
shift /3
)
shift /3
goto :loop
)
set -
:: To get the value of a single parameter, just remember to include the `-`
echo The value of -username is: !-username!
This solution provides Unix style arguments within a Windows batch. This is not the norm for Windows – batch usually has the options preceding the required arguments and the options are prefixed with /
.
The techniques used in this solution are easily adapted for a Windows style of options.
- The parsing loop always looks for an option at
%1
, and it continues until arg 1 does not begin with/
- Note that SET assignments must be enclosed within quotes if the name begins with
/
.SET /VAR=VALUE
failsSET "/VAR=VALUE"
works. I am already doing this in my solution anyway. - The standard Windows style precludes the possibility of the first required argument value starting with
/
. This limitation can be eliminated by employing an implicitly defined//
option that serves as a signal to exit the option parsing loop. Nothing would be stored for the//
“option”.
Update 2021-12-28: Support for !
in option values
In the code above, each argument is expanded while delayed expansion is enabled, which means that !
are most likely stripped, or else something like !var!
is expanded. In addition, ^
can also be stripped if !
is present. The following small modification to the un-commented code removes the limitation such that !
and ^
are preserved in option values.
@echo off
setlocal enableDelayedExpansion
set "options=-username:/ -option2:"" -option3:"three word default" -flag1: -flag2:"
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop
if not "%~3"=="" (
set "test=!options:*%~3:=! "
if "!test!"=="!options! " (
echo Error: Invalid option %~3
) else if "!test:~0,1!"==" " (
set "%~3=1"
) else (
setlocal disableDelayedExpansion
set "val=%~4"
call :escapeVal
setlocal enableDelayedExpansion
for /f delims^=^ eol^= %%A in ("!val!") do endlocal&endlocal&set "%~3=%%A" !
shift /3
)
shift /3
goto :loop
)
goto :endArgs
:escapeVal
set "val=%val:^=^^%"
set "val=%val:!=^!%"
exit /b
:endArgs
set -
:: To get the value of a single parameter, just remember to include the `-`
echo The value of -username is: !-username!
Working with environment variables
If you have variables that would be used across batch files, then it is always preferable to use environment variables. Once the environment variable is defined, it can be accessed via the % sign. The following example shows how to see the JAVA_HOME defined on a system. The JAVA_HOME variable is a key component that is normally used by a wide variety of applications.
Working with numeric values
In batch script, it is also possible to define a variable to hold a numeric value. This can be done by using the /A switch.
The following code shows a simple way in which numeric values can be set with the /A switch.