Command Line Iteration Statements
When you want to execute a command or a series of commands
repeatedly, you’ll use the for statement. The for statement is a powerful construct, and before you skip
this section because you think you know how the for
statement works, think again. The for statement is
designed specifically to work with the command-shell environment and is very
different from any other for statement you may have worked
with in other programming languages. Unlike most other for
statements, the one in the command line is designed to help you iterate through
groups of files and directories, and to parse text files, strings, and command
output on a line-by-line basis.
Iteration Essentials
The command shell has several different forms of for statements. Still, the basic form of all for statements is
for iterator do (statement)
Here the iterator is used to control the execution of the for loop. For each step or element in the iterator, the
specified statement is executed. The statement can be a single command or multiple commands
chained, piped, or grouped within parentheses.
The iterator usually consists of an initialization variable and a
set of elements to execute against, such as a group of files or a range of
values to step through. Initialization variables are essentially placeholders
for the values you want to work with. When you work with initialization
variables, keep in mind the following:
-
Iterator variables only exist within the context of a for loop.
-
Iterator variable names must be in the range from a to z or A to Z, such as %%A, %%B, or %%C.
As Table 3-4
shows, the various structures used with for statements
have specific purposes and forms. When the for statement
is initialized, iterator variables, such as %%B, are
replaced with their actual values. These values come from the element set
specified in the for statement and could consist of a list
of files, a list of directories, a range of values, and so on.
Iteration Purpose
|
Form Syntax
|
---|---|
Sets of files
|
for %%variable in (fileSet) do statement
|
Sets of directories
|
for /D %%variable in (directorySet) do
statement
|
Files in subdirectories
|
for /R [path] %%variable in (fileSet) do statement
|
Stepping through a series of values
|
for /L %%variable in (stepRange) do statement
|
Parsing text files, strings, and command output
|
for /F [“options”] %%variable in (source) do statement
|
Real World |
The forms provided are for scripts.
You can also use for statements interactively at the command line. In this case,
use %variable instead of %%variable.
Beyond this, for statements within scripts or at the
command line are handled in precisely the same
way.
|
Stepping through a Series of Values
The “traditional” way to use for
statements is to step through a range of values and perform tasks using these
values. You can do this in the command shell and the basic syntax of this type
of for loop is
for /l %%variable in (start,step,end) do (statement)
This type of for statement operates as
follows. First, the command shell initializes internal start, step, and end variables to the values you’ve specified. Next, it
compares the start value with the end value to determine if the statement should
be executed, yielding a true condition if the start value can be incremented or
decremented as specified in the step and a false condition otherwise. In the
case of a true condition, the command shell executes the statement using the
start value, then increments or decrements the start value by the step value
specified and afterward, repeats this process. In the case of a false condition,
the command shell exits the for statement, moving on to
the next statement in the script.
Consider the following example that counts from 0 to 10 by
2’s:
for /l %%B in (0,2,10) do echo %%B
The output is
0246810
You can also use a negative step value to move through a range in
decreasing values. You could count from 10 to 0 by 2’s as follows:
for /l %%B in (10,-2,0) do echo %%B
The output is
1086420
Iterating Through Groups of Files
A more powerful way to use for
statements in the command shell is to use them to work with files and
directories. The for statement syntax for working with
groups of files is
for %%variable in (fileSet) do (statement)
Here you use fileSet to specify a set of
files that you want to work with. A file set can be
-
Individual files as specified by a filename, such as MyFile.txt
-
Groups of files specified with wildcards, such as *.txt
-
Multiple files or groups of files with spaces separating file names, such as *.txt *.rtf *.doc
Now that you know the basic rules, working with files is easy. For
example, if you want to list all text files in an application directory, you can
use the following command in a script:
for %%B in (C:\Working\*.txt) do (echo %%B)
Here B is the initialization variable, C:\Working\*.txt specifies
that you want to work with all text files in the C:\Working directory, and the
statement to execute is echo %%B, which tells the command
shell to display the current value of %%B each time it
iterates through the for loop. The result is that a list
of the text files in the directory is written to the output.
You could extend this example to examine all .txt, .rtf, and .doc
files like this:
for %%B in (%AppDir%\*.txt %AppDir%\*.rtf %AppDir%\*.doc) do (echo%%B)
You can also use multiple commands using piping, grouping, and
chaining techniques, such as
for %%B in (%AppDir%\*.txt %AppDir%\*.rtf %AppDir%\*.doc) do (echo %%B& move C:\Data)
Here you list the .txt, .rtf, and .doc files in the location
specified by the AppDir variable and then move the files
to the C:\Data directory.
Iterating Through Directories
If you want to work with directories rather than files, you
can use the following for statement style:
for /d %%variable in (directorySet) do (statement)
Here you use directorySet to specify the
group of directories you want to work with. Iterating directories works exactly
like iterating files, except you specify directory paths rather than file paths.
If you wanted to list all the base directories under %SystemRoot%, you would do this as follows:
for /d %%B in (%SystemRoot%\*) do echo %%B
On Windows Server 2003, a partial result list would be similar
to
C:\Windows\AppPatchC:\Windows\ClusterC:\Windows\ConfigC:\Windows\CursorsC:\Windows\Debug
Note |
Note that the for /d loop iterates
through the specified directory set but doesn’t include subdirectories of those
directories. To access subdirectories (and indeed the whole directory tree
structure), you use for /r loops, which I’ll discuss in a
moment.
|
You can specify multiple base directories by separating the
directory names with spaces, such as
for /d %%B in (%SystemRoot% %SystemRoot%\*) do echo %%B
Here you examine the %SystemRoot% directory
itself and then the directories immediately below it. So now your list of
directories would start with C:\Windows (if this is the system root) and
continue with the other directories listed previously.
You can also combine file and directory iteration techniques to
perform actions against all files in a directory set, such as
for /d %%B in (%APPDATA% %APPDATA%\*) do (@for %%C in ("%%B\*.txt") do echo %%C)
The first for statement returns a list of
top-level directories under %APPDATA%, which also includes
%APPDATA% itself. The second for
statement iterates all .txt files in each of these directories. Note the @
symbol before the second for statement. As with if statements, this indicated the second for statement is nested and is required to ensure proper
execution. The double quotations with the file set ("%%B\*.txt") ensure that directory and file names containing
spaces are handled properly.
Because you’ll often want to work with subdirectories as well as
directories, the command shell provides for /r statements.
Using for /r statements, you can examine an entire
directory tree from a starting point specified as a path. The syntax is
for /r [path] %%variable in (fileSet) do (statement)
Here path sets the base of the directory tree you want to work
with, such as C:\. The path is not required, however, and if the path is
omitted, the current working directory is assumed.
Using a for /r statement, you could extend
the previous example to list all .txt files on the C: drive without needing a
double for loop, as shown here:
for /r C:\ %%B in (*.txt) do echo %%B
As you can see, for /r statements are
simpler than double for loops and more powerful. You can
even combine /r and /d without
needing a double loop. In this example, you obtain a listing of all directories
and subdirectories under %SystemRoot%:
for /r %SystemRoot% /d %%B in (*) do echo %%B
Parsing File Content and Output
Just as you can work with file and directory names, you can
also work with the contents of files and the output of commands. To do this,
you’ll use the following for statement style:
for /f ["options"] %%variable in (source) do (statement)
Here, options sets the text matching
options, source specifies where the text comes from, which
could be a text file, a string, or command output, and statement specifies what commands should be performed on
matching text. Each line of text in the source is handled like a record where
fields in the record are delimited by a specific character, such as a tab or a
space (which are the default delimiters). Using substitution, the command shell
then replaces placeholder variables in the statement with actual values.
Consider the following line of text from a source file:
William Stanek Engineering Williams@adatum.com 3408
One way of thinking of this line of text is as a record with five
fields:
-
First Name William
-
Last Name Stanek
-
Department Engineering
-
E-Mail Address Williams@adatum.com
-
Phone Extension 3408
To parse this and other similar lines in the associated file, you
could use the following for statement:
for /f "tokens=1-5" %%A in (current-users.txt) do (@echo Name: %%A %%B Depart: %%C E-mail: %%D Ext: %%E)
Here you specify that you want to work with the first five fields
(token fields separated by spaces or tabs by default) and identified by iterator
variables, starting with %%A, which means the first field
is %%A, the second %%B, and so on.
The resulting output would look like this:
Name: William Stanek Depart: Engineering E-Mail: Williams@adatum.comExt: 3408
Table 3-5 shows a
complete list of options that you can use. Examples and descriptions of the
examples are included.
Option
|
Description
|
Example
|
Example Description
|
---|---|---|---|
eol
|
Sets the end-of-line comment character. Everything after the
end-of-line comment character is considered to be a comment.
|
"eol=#"
|
Sets # as the end-of-line comment character.
|
skip
|
Sets the number of lines to skip at the beginning of
files.
|
"skip=5"
|
Tells the command shell to skip lines 1 through 5 in the
source file.
|
delims
|
Sets delimiters to use for fields. The defaults are space
and tab.
|
"delims=,.:"
|
Specifies that commas, periods, and colons are
delimiters.
|
tokens
|
Sets which token fields from each source line are to be
used. You can specify up to 26 tokens provided you start with a or A as the
first iterator variable. By default, only the first token is examined.
|
"tokens=1,3"
|
|
"tokens=2-5"
|
First example sets fields to use as 1 and 3. Second example
sets fields 2, 3, 4, and 5 as fields to use.
|
||
usebackq
|
Specifies that you can use quotation marks in the source
designator: double quotation marks for file names, back quotation marks for
command to execute, and single quotation marks for a literal string.
|
"usebackq"
|
—
|
To see how additional options can be used, consider the following
example:
for /f "skip=3 eol=; tokens=3-5" %%C in (current-users.txt) do (@echo Depart: %%C E-mail: %%D Ext: %%E)
Here, three options are used. The skip
option is used to skip the first three lines of the file. The eol option is used to specify the end-of-line comment
character as a semicolon (;). Finally, the tokens option
specifies that tokens 3 to 5 should be placed in iterator variables, starting
with %%C.
With tokens, you can specify which fields you want to work with in
many different ways. Here are some examples:
-
tokens=2,3,7 Use fields 2, 3, and 7.
-
tokens=3-5 Use fields 3, 4, and 5.
-
tokens=* Examine each line in its entirety and do not break into fields.
When you work with text files, you should note that all blank
lines in text files are skipped and that multiple source files can be specified
with wild cards or by entering the file names in a space-separated list, such
as
for /f "skip=3 eol=; tokens=3-5" %%C in (data1.txt data2.txt) do (@echo Depart: %%C E-mail: %%D Ext: %%E)
If a file name contains a space or you want to execute a command,
specify the usebackq option and quotation marks, such
as
for /f "tokens=3-5 usebackq" %%C in ("user data.txt") do (@echo Depart: %%C E-mail: %%D Ext: %%E)
or
for /f "tokens=3-5 usebackq" %%C in (`type "user data.txt"`) do (@echo Depart: %%C E-mail: %%D Ext: %%E)
Tip |
Remember the backquote (`) is used with commands and the
single quotation mark (‘) is used with string literals. In print, these
characters no doubt look very similar. However, on a standard keyboard, the
backquote (`) is on the same key as the tilde (~) and the single quotation mark
(‘) is on the same key as a double quotation mark
(“).
|
Note |
In the second example, I use the TYPE command to write the
contents of the file to standard output. This is meant to be an example of using
a command with the backquote.
|
Speaking of quotation marks, you use quotation marks when you want
to process strings and variable values. Here, you enclose the string or variable
name you want to work with in double quotation marks to ensure the string or
variable can be evaluated properly. You do not, however, need to use the usebackq option.
Consider the following example:
set value=All,Some,Nonefor /f "delims=, tokens=1,3" %%A in ("%VALUE%") do (echo %%A %%B)
The output is
All None
No comments:
Post a Comment