A batch file has one kind of variable: a named string in the environment of the
cmd.exe process, which child programs inherit. There are no types, arrays or scopes
other than setlocal. The syntax on this page follows Microsoft’s
set,
setlocal
and cmd
pages; the substring and replacement forms are from the interpreter’s own set /? help and
the dynamic variables from SS64.
set
| Form | Effect |
|---|---|
set | Display all variables |
set P | Display the variables whose names start with P (needs command extensions) |
set NAME=value | Set a variable. Everything after the = up to the end of the line is the value, spaces and quotes included |
set NAME= | Delete the variable |
set "NAME=value" | Set a variable and keep out trailing spaces and unquoted special characters: the quotes are not part of the value. This is the safe form |
set /p NAME=prompt | Show the prompt and store the line typed; an empty answer leaves the variable as it was. The input can come from a redirection |
set /a NAME=expression | Evaluate a number expression and store the result |
- Names are not case sensitive but
setshows them as typed. A name can hold most characters except=, and spaces count:set a = 1sets a variable nameda(with a space) to1. - Because
< > | & ^are special, they need a caret or quotes in a value. Microsoft:set v=New^&NamegivesNew&Name, andset v="New&Name"keeps the quotes in the value. - Limits (Microsoft’s
cmdpage): one variable up to 8,192 bytes, and the whole environment of a process up to 65,536 characters; the longest line is 8191 characters. - A variable set in a batch file lasts for the
cmd.exeprocess, and is not passed up to the parent that started it.
Expansion
| Syntax | Result |
|---|---|
%NAME% | The value. In a batch file an unset variable becomes an empty string; at the prompt it is left as typed. Expansion is done once, not recursively (Microsoft) |
%NAME:~n% | The value from character n (counting from 0) to the end |
%NAME:~n,m% | m characters starting at n |
%NAME:~-n% | The last n characters |
%NAME:~n,-m% | From n, leaving off the last m characters |
%NAME:old=new% | The value with every old replaced by new, whatever the case; new may be empty to delete text |
%NAME:*old=new% | Everything up to and including the first old replaced by new |
These forms need command extensions (on by default). A variable that is not set
expands to nothing, which is why a missing variable in if %x%==1 makes a syntax error:
write if "%x%"=="1".
The command line of a batch file argument is %1, not a variable; see
Parameters and labels.
Delayed expansion
Normal expansion happens when a line, or a whole parenthesised block, is read, before any command in
it runs. So a variable changed inside a block or a for body still has its old value in the
same block:
set n=1
if 1==1 (
set n=2
echo %n%
setlocal enabledelayedexpansion
echo !n!
)
%n% prints 1 because the block was expanded before set n=2 ran; !n! prints 2.- Turn it on with
setlocal enabledelayedexpansion(untilendlocal), withcmd /v:on, or with a registry setting;disabledelayedexpansionand/v:offturn it off. It is off by default. - When it is on,
!NAME!is replaced when the command runs.!NAME:~n,m!and!NAME:old=new!work as with percent signs. - Every
!in a line becomes special while it is on. To keep a literal one, write^^!(or quote the text and write^!). Values that hold a!, such as file names, can be damaged by a later expansion. - Loop variables such as
%%iare expanded in an earlier pass than!var!, so!array[%%i]!works.
set /a
The expression is evaluated with whole numbers; a name in it is taken as a variable and its value is
read as a number, and a name that is not set counts as 0, so % signs are not needed. The
operators are, from the strongest binding to the weakest (Microsoft):
| Operators | Meaning |
|---|---|
( ) | Grouping |
! ~ - | Unary: logical not, bitwise not, negation |
* / % | Multiply, divide, remainder |
+ - | Add, subtract |
<< >> | Shift left, shift right |
& | Bitwise and |
^ | Bitwise exclusive or |
| | Bitwise or |
= *= /= %= += -= &= ^= |= <<= >>= | Assignment and the compound assignments |
, | Separates expressions: set /a a=1, b=2 |
- Numbers are decimal, hexadecimal with
0x, or octal with a leading0:0x12,18and022are equal (Microsoft). So08and09are errors. - Arithmetic is reported to use 32-bit signed integers, so results wrap and division truncates toward zero. Microsoft does not state the width.
- The characters
&,|,^,<,>and in a batch file the remainder%(written%%) need quotes or carets:set /a "x=a|b". Microsoft says to quote an expression that uses&&,||or%. - Typed at the prompt,
set /aalso prints the result; in a batch file it does not. - There is no floating point, no string arithmetic and no exponent operator.
Special variables
These are computed each time they are expanded, are not stored in the environment, and are missing when command extensions are disabled (SS64).
| Variable | Value |
|---|---|
%ERRORLEVEL% | The exit code of the last program or command (Errorlevel) |
%CD% | The current directory, without a closing backslash except at a drive root |
%DATE%, %TIME% | The current date and time in the regional format of date and time; a time may contain a comma |
%RANDOM% | A whole number from 0 to 32767 |
%CMDEXTVERSION% | The command extensions version: 1 on Windows NT, 2 from Windows 2000 |
%CMDCMDLINE% | The original command line that started this cmd.exe |
%HIGHESTNUMANODENUMBER% | The highest NUMA node number of the computer |
%__CD__%, %__APPDIR__% | Undocumented: the current directory, and the directory of the running program, each with a closing backslash |
%=C:%, %=D:% | Undocumented: the current directory on that drive |
%=ExitCode%, %=ExitCodeAscii% | Undocumented: the last exit code in hexadecimal, and as a character |
%__COMPAT_LAYER%, %FIRMWARE_TYPE% | Undocumented: compatibility layer (read and write), and Legacy or UEFI |
Microsoft’s if page adds that if defined makes %errorlevel%,
%cmdcmdline% and %cmdextversion% available as environment variables, and that a
real variable of the same name takes precedence over the dynamic one.
Windows fills the environment with ordinary variables, which a script may rely on but Microsoft’s
command pages do not list: COMSPEC (the path of cmd.exe), PATH,
PATHEXT, PROMPT, DIRCMD (default dir options),
TEMP and TMP, USERNAME, USERPROFILE,
APPDATA, LOCALAPPDATA, COMPUTERNAME, SystemRoot and
windir, ProgramFiles and ProgramFiles(x86),
NUMBER_OF_PROCESSORS, PROCESSOR_ARCHITECTURE and OS
(Windows_NT). The order of precedence given by Microsoft’s cmd page: built-in
system variables, then the system and user registry variables, then Autoexec.bat, then a
logon script, then variables set in the script.
setlocal and endlocal
setlocal [enableextensions | disableextensions] [enabledelayedexpansion | disabledelayedexpansion]
starts a local environment in a batch file: variable changes, and changes to the current directory,
made after it are undone by the matching endlocal or at the end of the file (Microsoft).
setlocalandendlocalcan nest, and eachcalled subroutine file has its own.- The four options set command extensions and delayed expansion until the matching
endlocal, whatever they were before. - Used with an option,
setlocalsetsERRORLEVELto 0, otherwise to 1: a test for whether extensions are available (Microsoft’s example usesverify other 2>nulfirst). - At the prompt, outside a script, both do nothing.
- To carry a value out of a local environment, expand it on the same line as
endlocal, which is read before it runs:endlocal & set result=%result%.
An example
@echo off
setlocal enabledelayedexpansion
set "count=0"
for %%f in (*.txt) do (
set /a count+=1
set "name[!count!]=%%~nxf"
)
for /l %%i in (1,1,%count%) do echo %%i: !name[%%i]!
set "text=Hello, World"
echo %text:~0,5% %text:World=there% %text:~-5%
endlocal & set "files=%count%"
setlocal.Not described
Unknown
- Reference: the width of
set /anumbers, the undocumented dynamic variables and theendlocalcarry-out idiom are from SS64 and community analysis, not Microsoft, and were not run oncmd.exefor this page.