There are four distinct types of Windows(well nt based anyway) batch for loops, the "conventional" loop, the "counting" loop(/L) , the "walking" loop(/R) and the"parsing" loop (/F), I will try to impart knowledge about all of them here. There is also "/D", but I consider that a modifier rather than a distinct type.
All this relates to Windows 2000 and above...
Be sure to check out %variable% !catches! after reading this page.
I will start out with some useful info and then get on to the loops. I suggest reading this page in order.
Content on this page:
Delimiters - The Standard Delimiters
Problem Characters - Some Characters to Watch Out For
Modifiers - For Variable Modifiers
Conventional For - The Conventional For Loop
For /L - The Counting Loop
For /R - The Walking Loop
For /F - The Parsing Loop
There are cases where only a space will do or delimiters are user defined, but these are what I will refer to as the "standard delimiters":
Space " " Tab " " Equals sign "=" Comma "," Semi-colon ";"
These characters all count as a space on the command line or delimiter on the command line and in a set unless they are enclosed in double quotes.
Generally in batch there are a few problem characters:
Less than symbol "<"
Greater than symbol ">"
Percentage sign "%"
Equals Sign "="
Double quote """
Exclamation mark "!"
Left bracket "("
Right bracket ")"
Handling these characters is often specific to the situation and in a small number of situations not even possible.
There are three ways of handling these, some characters can only use one or two of the work arounds.
Escaping - The caret(^) is the escape character, using one before the character(including that caret itself) escapes it. When delayed expansion is enabled two carets are needed for exclamation marks, see %variable% !catches!.
Doubling - Normally used for percentage signs, as the name suggests you simple input two where you want one.
Quoting - Where possible, like filenames, surround the string in double quotes.
It may take some trial and error to find the best solution for your situation.
When used in a batch script for loop variables use two percentage signs, on the command line only one.
All of the for loops have variable syntax in common, so here I will list the Modifiers:
%%~ - Remove Surrounding quotes(true for all modifiers).
%%~a - Attributes of the file or folder.
%%~d - Drive letter of the variable.
%%~f - Fully qualified path name.
%%~n - File or folder name without the extension or path.
%%~p - Path only; no file name.
%%~s - Short file name alias; 8.3 format.
%%~t - File date and time(local format)
%%~x - File extension only.
%%~z - File size, a directory will always return 0.
%%~$variable: - Commonly used with the path variable, but can be any variable with a delimited list of folder names. It finds and returns the first matching file name contained in the list of folders.
Some of these can be compounded like so: %%~nx, in the case last example the modifiers go before the percentage sign.
The character that is used as the variable letter goes after the modifiers. When to use the modifiers generally depends on the content of the variable.
These modifiers are also valid for arguments, but are used with a single percentage sign and the argument number in place of the variable character.
The conventional loop simply does the same thing with each part of the set:
for %%a in (a b c d e f g) do echo %%a
The basic syntax is the same for all of the loops:
for [possibly options] %%[variable declaration] in ([data]) do [commands]
We will ignore "[possibly options]" for now and look at "[variable declaration]".
For loops always use a single character variable, normally a -z or A - Z, but it can be most printable ASCII characters. The variable character is case sensitive so "%%a" is not the same as "%%A". This single character variable is marked with two percentage signs(or one if at the command line) both when declared and when expanded, though the modifiers mentioned above can be present if required when expanding the variable in the "do" section of the loop.
It starts to get a little confusing with "[data]" because it can contain either a static set, static file name or wild card character to iterate over files that exist in a directory.
When using a static set as above the for loop will split the string inside the "[data]" section on the "standard delimiters" unless the string is double quoted, in which case the string, quotes and all, get passed as a single element to the "do" portion of the loop.
Here is an example to run that may help to build understanding:
for %%a in ("one set;;;" 2;3,4=5 ;"another,, set") do (
Notice how the quoted strings come out intact and the rest gets split up? The same principles apply to filenames that contain any of the problematic characters.
Another important thing to note about the example is how the "[commands]" section is split onto multiple lines using a "code block", this way multiple commands on multiple lines can be executed after do. The important thing to remember about code blocks is that they start with a left bracket "(" and end in a right bracket. Also see %variable% !catches!.
Now if we made each element into a file name then we could use a bunch of modifiers on them:
for %%a in ("cmd.exe" "find.exe") do (
As I mentioned above with wild card characters we can loop over a directory and the variable will become the file name of matching strings, we can use a combination of characters and wild cards, including wild cards alone:
* -Any character or any number of characters, it will also match for no characters at all.
? - One occurrence of any single character.
One of most common uses is "*.ext" or all files that end in ".ext".
The below script will iterate over each file the current directory.
for %%a in (*) do echo %%a
"[possibly options]" Now comes into play, the only option the conventional for loop will take it "/d". All it does is match wild cards to directory names instead of file names. "For /R" also supports this switch.
for /d %%a in ("%systemdrive%\*") do echo %%a
The "counting" loop is probably the easiest of the bunch; it doesn't support any extra switches at all:
For /L %%[variable declaration] in ([start], [step], [end]) do [commands]
"[start], [step], [end]" are simply three numbers separated by any of the standard delimiters mentioned above, but be aware they must be inside the natural signed word length of the processor and Operating system, otherwise they will be trimmed to the maximum or minimum number(−2147483648 to +2147483647 for 32bit systems).
The numbers can be either negative or positive depending on the the direction you want the counting to go. The loop will just skip execution if there are no numbers between [start] and [end] that can be returned.
"[start]" will always be the first number returned.
"[step]" is the number that will be added or subtracted with each iteration.
"[end]" is the highest or lowest number and last number that can be returned, depending on the step.
Now for a couple of examples:
for /l %%a in (1,1,10) do echo %%a
for /l %%b in (-10, 5, 100) do echo %%b
for /l %%c in (100, -1, 80) do echo %%c
for /l %%d in (-50, 1, -20) do echo %%d
People more familiar with C will point out that this is more of a "conventional" for loop than the previous; the reason I called the previous loop "conventional" is because it existed in a similar form in MS-DOS and the other loops did not.
For /l can also be used to create an "infinite" loop if the increment is set to 0. It is very difficult to effectively break from this loop in code, the flow of control will change after a goto or "exit /b" but the loop will continue to iterate pointlessly in the background. The only way I know of to break this loop is to press ^C, close the console of end the cmd process and the console associated with it.
FOR /L %%a in (0 0 0) do echo It doesn't stop!
.... or more succinctly
FOR /L %%a in (0) do echo It doesn't stop!
The "walking" loop, for /R, starts at a given directory and processes the do command at least once in each subdirectory, but can be more depending on the data specified.
For /R [possibly /d] [possibly a start directory] %%[variable declaration] in ([data]) do [command]
"[possibly /d]" Simply means the "for /R" loop can take the "/d" switch to match wild cards to directory names instead of file names.
"[possibly a start directory]" This is the directory to start processing at, if omitted the current directory is used instead, don't forget to double quote names with spaces or other special characters.
In this case "[data]" can be a dot "." to just enumerate over every folder, a "match-all" wild card to get every file or folder name(with /d), a set of mixed characters and wild cards or a set of straight characters. The difference in output are many:
This will just list every directory name stating at "c:\", the output will have a dot on the end, so you may want to use %%~dp:
for /r "c:\" %%a in (.) do echo %%a
And this will list every(well almost) file in the directories or just the directories if "/d" is present:
for /r "c:\" %%b in (*) do echo %%b
The below will match every file that matches either of the wildcard strings:
for /r "c:\" %%c in ("f?n*.ex*" "?m?.ex*") do echo %%c
The result for the below example is quite unexpected, it doesn't simply find the file name and return the only results. Instead it will pass back the file name appended to the directory with every iteration, regardless if the file exists or not:
for /r "c:\" %%d in ("find.exe") do echo %%d
Also note that "for /r" doesn't seem to return hidden or system files or folders.
Again the syntax of "for/f" is very similar, but it has it's own uses and quirks:
For /f [possibly options] %%[variable declaration] in ([data]) do [commands]
For /f generally runs over lines of text and can split up the lines into "chunks" of text on particular characters.
These options are specific to the for /f loop and, as a whole, should be enclosed in double quotes unless using special syntax to work around limitations(noted later on).
USEBACKQ - Changes the syntax of [data], using a new quoting scheme.
SKIP=N - Skips the first N line of input, this includes blank lines. Also note that N can't be less than one.
EOL=X - Tells the loop to ignore all lines that start with character X. It can only be a single character. EOL defaults to a semi-colon when not specified.
EOL can be disabled by setting it to character used as a delimiter, including the defaults of space and tab, or by using special syntax(noted later on). EOL can also be negated by using a command that prefixes all lines with predictable text, such as find /n /v "".
TOKENS=[token definition] - Tells the for loop how many and what specific tokens you want to use. A single for loop can use no more than 31 tokens without nesting. When not specified tokens default to "1".
"[token definition]"This can be a number, a range, a set of numbers a wild card or a combination, the wild card can only appear once and in must be the very last token definition.
DELIMS=[delim characters] - Delims should always be the last option because it allows a space to be entered as a delimiter if specified last. It is possible to Have no delimiter at all, ensuring the entire line is captured in the variable. When not specified "delims" default to a space and tab.
These are the characters that tokens of text are broken up on, these character are lost, with the exception of a variable derived from "*". If delims is the last option and there are no characters between the equals sign and final double quote then the string isn't split at all. If defining a space as a delimiter it should always be the last delimiter specified. If a tab is included it should also be the last delimiter unless a space is also included, in this case the space should be last and the tab second last. To use a double quote as a delimiter see the special syntax section(noted later on).
Again this is a single character, the main difference is that if multiple tokens are specified variables are also implicitly declared. The character used in the initial declaration will always match the first declared token, when multiple tokens are specified the next character in ASCII will implicitly defined with the next token until all tokens are set to a variable or no usable characters are left.
This is the reason a-z and A-Z are commonly used, it makes it easy to work out what the implicitly defined tokens will be. If the first token is held in %%a then the second will be %%b, the third is %%c and so on. It works this way because ASCII defined a-z contiguously.
Although it's possible to use most ASCII characters I would suggest that a-z and A-Z to keep things simple. 1-9 are also allowable, but I personally don't use these because they look so similar to argument variables.
Some clever people have figured out how to use a double as a delimiter and disable eol when no delimiters are used. I will give a brief outline of these techniques because they can be very handy to know.
The basis of these techniques is escaping, the entire option string can be specified without even using double quotes! The key is to escape all of the standard delimiters, the "Commonly problematic" problem characters and the double quote(if being used as a delim). This has to be done for the entire option string.
Here is an example of using the double quote as a delimiter with no other options specified:
for /f delims^=^" %%a in ("e"f"d") do echo %%a
The same again, but this time setting eol to a delim character(in this case a double quote) to disable it.
for /f eol^=^"^ delims^=^" %%a in ("e"f"d") do echo %%a
And using other options:
for /f tokens^=1^,2^,*^ delims^=^|^" %%a in ("e|4"f"d") do echo %%a - %%b + %%c
To disable eol and delimiters takes this syntax one step further, for this to work the option string now needs an empty line within it. This actually sets eol to LF(0x0A), which won't appear in a line because it is one of the linebreak characters.
The below example disables both eol and delimiters, the empty line is absolutely necessary for this to work.
for /f eol^=^
^ delims^= %%a in ("; e"f"d") do echo %%a
These techniques may look a bit ugly but they provide functionality otherwise not available.
The syntax of [data] is ruled over by the "usebackq" option. When "usebackq" is not present then the syntax is as follows.
Filenames - Inside the brackets with no quotes present, filenames that contain spaces or other special characters must either use 8DO3 filename aliases or use the usebackq option. Files must be ANSI encoded text files, though there are no guarantees for extended ANSI characters. Multiple filenames can be separated with any of the standard delimiters.
Command output - The command string must be surrounded by single quotes('). Ampersands(&), pipes(|), Less than symbols(<), greater than symbols(>) and bracket characters( ) or ( ) must be escaped with a caret(^) unless they are double quoted.
String - A sting must be enclosed in double quotes.
With the "usebackq" option these change to:
Filename - Either the same as without usebackq or double quoted.
Command output - The same as without "usebackq" except that it must be surrounded by back quotes(`) instead of single quotes(').
String - Strings must be enclosed with single quotes('). Because the double quotes can no longer escape certain characters they must be escaped with a caret(^).
if exist %fn% (
> %fn% echo Hi I'm a text file, read me.
rem Now were all set up.....
for /f "tokens=1" %%a in (%fn%) do echo %%a
for /f "tokens=3-7 delims=,. " %%a in (%fn%) do echo %%d %%e %%a %%b %%c
for /f "tokens=1,2,3 delims=HiI'x " %%a in (%fn%) do echo %%a%%b%%c
for /f "usebackq tokens=1*" %%a in ("%fn%") do echo %%b
>> %fn% echo Wow time to skip the intro
for /f "skip=1 tokens=2,4-6" %%a in (%fn%) do echo %%b %%c %%d %%a
for /f "usebackq eol=H tokens=4,6" %%a in ("%fn%") do echo %%b %%a
for /f "delims=" %%a in ('dir /b/a-d') do (
echo Look %%a is a file!
for /f "usebackq delims=" %%a in (` dir /b/a-d ^| find /c /v "" `) do (
echo You just hit enter %%a times - Ha ha
for /f "tokens=1-12" %%a in ("s h o e l t r g n i m a") do (
echo %%b%%d%%e%%e%%c %%j'%%k %%l %%a%%f%%g%%j%%i%%h...
for /f "usebackq tokens=3 delims=8" %%a in ('8pick8one8bean8') do (
There is a catch to watch out for when using the "%%~f" modifier when the variables contain only a filename without any drive or path details. If the file exists in another directory than the current directory then the path returned will be the current directory, here is an example:
cd /d "%systemdrive%"
for /f "delims=" %%a in (' dir /b/a-d "%systemroot%" ') do (
echo "%%a" - What %%%% a is
echo "%%~fa" using in %%%%~f
echo it should be "%systemroot%\%%a"