Batch Files


Batch files are, in essence, command-line commands typed into a text file rather than typed directly at the command prompt. You can tell the command shell to read this text file and interpret its commands as if you were typing them. This serves several purposes:

  • It can save you a lot of typing, as you only need to type the batch file commands once, but you can have Windows run the commands as many times as you want. This is really important with long sequences of commands, but I even more frequently write what I call "tiny handy batch files," with single letter names , and perhaps only one command inside, to do things like change the current directory to a commonly used folder. These batch files can save you quite a bit of time and trouble.

  • You can write the batch file in such a way that it records steps of a particular job without storing the particulars ; you can then provide the particulars when you use the batch file. For example, if you repeatedly have to extract and print information from different data files, you might write a batch file that performs these steps but doesn't contain the name of the actual data file to be processed . You can supply that name when you use the batch file. This, again, can save you time and mental energy: After the batch file is written, you don't have to think about what's inside the batch file. It's just a new Windows command that you can use whenever you need.

  • The contents of the batch file serve as a form of documentation, which lists the steps necessary to perform a particular job. A year after you've completed some project, you might have forgotten the steps involved in processing your data files, but you can always read the batch file yourself to refresh your memory.

Batch files can also use primitive programming commands to handle varying situations: "if a particular situation arises, do this, otherwise , do that" or "repeat this step for every file in a certain folder."

Creating and Editing Batch Files

You can place batch files in any folder you want. You can place them on your own hard drive, or you may want to place your batch files on a shared network folder so they can be used from any computer. I place my personal batch files in a folder named c:\bat and put this folder in the PATH so that I can run them by name from any command prompt.

Regardless of their folder location, batch files should be given the extension .CMD or .BAT . Either is fine. The .BAT extension is more traditional, whereas .CMD makes it clear that the batch file is written for Windows NT/2000/XP, because DOS and Windows 9x will not recognize such files as batch files.

To create a sample batch file, open a Command Prompt window or use the one opened earlier in this section. Type the command

 notepad test.bat 

and then click Yes when Notepad asks, Do you want to create a new file? In the empty Notepad window, type the following lines:

 @echo off cls echo The command line argument is %1 pause 

Save the file and at the command line type:

 test xxx 

The Command Prompt window should clear, and you should see the following:

 The command line argument is xxx Press any key to continue . . . 

Press Enter, and the command prompt should return. You can use this same procedure to create any number of batch files.

Later, if you want to edit the files, you can use the name notepad command to open and modify the files and make changes.

One batch file you may want to create right now is named bat.bat , with this one line inside:

 pushd c:\bat 

With this file in place, if you type bat at any command prompt, your current directory will be instantly changed to c:\bat so that you can edit or create new batch files. Type popd to go back to whatever directory you were using beforehand.

Of course, you could just type pushd c:\bat directly. It may seem silly to create a batch file just to save nine keystrokes, but when you're actively developing batch files, you'll quickly find that this really does make life easier.

I have about a dozen batch files like this that I use on a daily basis to move into directories for specific projects. For projects that use special command-line programs, the batch file has a second line that adds the program directory to the beginning of the search path, using a command such as this:

 path c:\some\new\folder;%path% 

If you find yourself frequently working with command-line programs, you will want to make "tiny handy batch files" for your projects as well.

Batch File Programming

The following sections discuss programming techniques that take advantage of the extended commands provided by the cmd shell. The commands that are most useful in batch files are listed in Table 9.12.

Table 9.12. Batch File Commands

Command

Use

call

Calls a batch file subroutine

echo

Displays text to the user

setlocal/endlocal

Saves/ restores environment variables

exit

Terminates batch file

for

Iterates over files, folders, or numbers

goto

Used for flow control

if/else

Executes commands conditionally

pause

Lets the user read a message

pushd/popd

Saves/restores the current directory

rem

Used for comments and documentation. Windows ignores any lines that start with the word rem or remark .

set

Sets environment variables, performs calculations, and prompts for user input

shift

Scans through command-line arguments

start

Launches a program in a different window


If you've written batch files for DOS, Windows 9x, and Windows NT, you'll find that most of these commands have been significantly enhanced, so even if you are familiar with these commands, you should read the discussions in this chapter, and check out the online Command Line Reference in Windows Help and Support.

Argument Substitution

Many times you'll find that the repetitive tasks you encounter use the same programs and steps, but operate on different files each time. In this case, you can use command-line arguments to give information to the batch file when you run it. When you start a batch file from the command line with a command such as

 batchname xxx yyy zzz 

any strings after the name of the batch file are made available to the batch program as arguments. The symbols %1, %2, %3 , and so on are replaced with the corresponding arguments. In this example, anywhere that %1 appears in the batch file, cmd replaces it with xxx . Then, %2 is replaced with yyy , and so on.

Argument substitution lets you write batch files like this:

 @echo off notepad %1.vbs cscript %1.vbs 

This batch file lets you edit and then run a Windows Script Host program. If you name the batch file ws.bat , you can edit and test a script program named, say, test.vbs just by typing this:

 ws test 

In this case, cmd treats the batch file as if it contained the following:

 @echo off notepad test.vbs cscript test.vbs 

This kind of batch file can save you many keystrokes during the process of developing and debugging a script.

Note

The command @echo off at the top of a batch file keeps cmd from printing out each of the program steps as it encounters them. This reduces "clutter" in the Command Prompt window. If your batch file is behaving in some way you don't understand, you can temporarily change this to @echo on , to see the steps as they are run.


Besides the standard command-line arguments %1, %2 , and so on, you should know about two special argument replacements : %0 and %*. %0 is replaced with the name of the batch file, as it was typed on the command line. %* is replaced with all the command-line arguments as they were typed, with quotes and everything left intact.

If I have a batch file named test.bat with the contents

 @echo off echo The command name is %0 echo The arguments are: %* 

then the command test a b c will print the following:

 The command name is test The arguments are: a b c 

%0 is handy when a batch file has detected some problem with the command-line arguments the user has typed and you want it to display a "usage" message. Here's an example:

 if "%1" == "" (     rem - no arguments were specified. Print the usage information     echo Usage: %0 [-v] [-a] filename ...     exit /b ) 

If the batch file is named test.bat , typing test would print out the following message:

 Usage: test [-v] [-a] filename ... 

The advantage of using %0 is that it will always be correct, even if you rename the batch file at a later date and forget to change the "usage" remarks inside.

Argument Editing

cmd lets you modify arguments as they're replaced on the command line. Most of the modifications assume that the argument is a filename and let you extract or fill in various parts of the name. You might want to use this feature when, say, a batch file argument specifies a Microsoft Word file with the .DOC extension, and you want the batch file to construct a new filename with the same root name but a different extension.

When command extensions are enabled, cmd can insert edited versions of the arguments by placing a tilde ( ~ ) and some additional characters after the % sign. The editing functions let you manipulate filenames passed as arguments. Table 9.13 lists the edit options, using argument number 1 as an example.

Table 9.13. Argument Editing Expressions

Expression

Result

%~1

Removes surrounding quotes ( " ).

%~f1

Fully qualified pathname.

%~d1

Drive letter only.

%~p1

Path only.

%~n1

Filename only.

%~x1

File extension only.

%~s1

Short DOS 8.3 file and path.

%~a1

File attributes.

%~t1

Modification date/time of file.

%~z1

Length of file in bytes.

%~$PATH:1

Fully qualified name of the first matching file when searching PATH . If no file is found, the result is a zero-length string. The filename must include the proper extension; PATHEXT is not used.


For example, if I ran a batch file with the argument "under the hood.doc" , the results might be as follows :

Expression

Result

%~1

under the hood.doc

%~f1

C:\book\ch11\under the hood.doc

%~d1

C:

%~p1

\book\ch11

%~n1

under the hood

%~x1

.doc

%~s1

C:\book\ch11\UNDERT~1.DOC

%~a1

--a------

%~t1

04/20/2002 12:42 PM

%~z1

45323

%~$PATH:1

 

Here's how these features might be used: Suppose I have a series of files that I need to sort . The input files could come from any folder, but I want to store the sorted files in C:\sorted and give them the extension .TAB regardless of what the original extension was. I can write a batch file named sortput.bat to do this:

 @echo off sort <%1 >c:\sorted\%~n1.tab 

The sort command will read the file as I've specified it on the command line, but the output file will use only the base part of the input file's name. If I run the command

 sortput "c:\work files\input.txt" 

the substituted command will be

 sort <"  c:\work files\input.txt  " >c:\sorted\  input  .tab 

Conditional Processing with if

One of the most important capabilities of any programming language is the ability to choose from among different instructions based on conditions the program finds as it runs. For this purpose, the batch file language has the if command.

The Basic if Command

In its most basic form, if compares two strings and executes a command if the strings are equivalent:

 if  string1  ==  string2 command  

This is used in combination with command-line variable or environment variable substitution, as in this example:

 if "%1" == "ERASE" delete somefile.dat 

If and only if the batch file's first argument is the word ERASE , this command will delete the file somefile.dat .

The quotation marks in this command aren't absolutely required. If they are omitted and the command is written as

 if %1 == ERASE delete somefile.dat 

the command will still work as long as some command-line argument is given when the batch file is run. However, if the batch file is started with no arguments, %1 would be replaced with nothing, and the command would turn into this:

 if == ERASE delete somefile.dat 

This is an invalid command. cmd expects to see something before the == part of the command and will bark if it doesn't. Therefore, it's a common practice to surround the items to be tested with some characterany character. Even $ will work, as shown here:

 if $%0$ == $ERASE$ delete somefile.dat 

If the items being tested are identical, they will still be identical when surrounded by the extra characters. If they are different or blank, you'll still have a valid command.

The if command also lets you reverse the sense of the test with the not option:

 if not "%1" == "ERASE" then goto no_erase 

Checking for Files and Folders

The exist option lets you determine whether a particular file exists in the current directory:

 if exist input.dat goto process_it     echo The file input.dat does not exist     pause     exit /b :process_it 

Of course, you can specify a full path for the filename if that's appropriate, and you can use environment variables and % arguments to construct the name. If the filename has spaces in it, you'll need to surround it with quotes.

The not modifier can be used with exist as well.

Tip

The exist test only checks for files, not folders. However, the special file nul appears to exist in every folder. You can perform the test

 if exist c:\foldername\nul  command  

to see whether the folder c:\foldername exists.


Checking the Success of a Program

When a command line or even a Windows program exits, it leaves behind a number called its exit status or error status value. This is a number that the program uses to indicate whether it thinks it did its job successfully. An exit status of zero means no problems; larger numbers indicate trouble. There is no predetermined meaning for any specific values. The documentation for some programs may list specific error values and give their interpretations, which means that your batch files can use these values to take appropriate action. How? Through the errorlevel variation of the if command.

After running a command in a batch file, an if statement of the form

 if errorlevel  number command  

will execute the command if the previous program's exit status value is the listed number or higher . For example, the net use command returns if it is able to map a drive letter to a shared folder, and it will return a nonzero number if it can't. A batch file can take advantage of this as follows:

 @echo off net use f: \bali\corpfiles if errorlevel 1 goto failed     echo Copying network data...     if not exist c:\corpfiles\nul mkdir c:\corpfiles     copy f:\*.xls c:\corpfiles     exit /b :failed     echo Unable to access network share \bali\corpfiles     pause 

You can also use not with this version of the if command. In this case, the command is executed if the error status is less than the listed number. The error testing in the previous example can be rewritten this way:

 if not errorlevel 1 goto success     echo Unable to access network share \bali\corpfiles     pause     exit /b :success     echo Copying network data...     if not exist c:\corpfiles\nul mkdir c:\corpfiles     copy f:\*.xls c:\corpfiles 

In this version, the flow of the batch file is a bit easier to follow. However, even this can be improved upon, as you'll see next .

Grouping Commands with Parentheses

Often, you'll want to execute several commands if some condition is true. In the old days, before the extended cmd shell came along, you would have to use a goto command to transfer control to another part of the batch file, as in the if exist example given in the previous section. With the extended version of if , this is no longer necessary.

The extended if command lets you put more than one statement after an if command, by grouping them with parentheses. For example, you can place multiple commands on one line, as shown here:

 if not errorlevel 1 (echo The network share was not available & exit /b) 

Or you can put them on multiple lines:

 if not errorlevel 1 (     echo The network share was not available     pause     exit /b ) 

I recommend the second version, because it's easier to read. Look how much clearer the network file copying example becomes when parentheses are used instead of goto :

 @echo off net use f: \bali\corpfiles if errorlevel 1 (     echo Unable to access network share \bali\corpfiles     pause     exit /b ) echo Copying network data... if not exist c:\corpfiles\nul mkdir c:\corpfiles copy f:\*.xls c:\corpfiles 

You can also execute one set of commands if the if test is true and another if the test is false by using the else option, as in this example:

 if exist input.dat echo input.dat exists  else  echo input.dat does not exist 

You can use else with parentheses, but you must take care to place the else command on the same line as if , or on the same line as the closing parenthesis after if . You should write a multiple-line if...else command using the same format as this example:

 if exist input.dat (     echo Sorting input.txt...     sort <input.txt >source.data ) else (     echo Input.txt does not exist. Creating an empty data file...     echo. >source.data ) 

Extended Testing

The extended if command lets you perform a larger variety of tests when comparing strings, and it can also compare arguments and variables as numbers. The extended comparisons are listed in Table 9.14.

Table 9.14. Comparison Operators Allowed by the if Command

Variation

Comparison

if string1 EQU string2

Exactly equal

if string1 NEQ string2

Not equal

if string1 LSS string2

Less than

if string1 LEQ string2

Less than or equal to

if string1 Gtr string2

Greater than

if string1 GEQ string2

Greater than or equal to

if /i (comparison)

Not case sensitive

if defined name

True if there is an environment variable name

if cmdextversion number

True if the cmd extensions are version number or higher


As a bonus, if the strings being compared contain only digits, cmd compares them numerically . For example, you could test for a specific exit status from a program with a statement like this:

 some program if %errorlevel% equ 3 (     echo The program returned an exit status of 3 which     echo means that the network printer is offline. ) 

Processing Multiple Arguments

When you have many files to process, you may get tired of typing the same batch file commands over and over, like this:

 somebatch file1.dat somebatch file2.dat somebatch file3.dat ... 

It's possible to write batch files to handle any number of arguments on the command line. The tool to use is the shift command, which deletes a given command-line argument and slides the remaining ones down. Here's what I mean: Suppose I started a batch file with the command line

 batchname xxx yyy zzz 

Inside the batch file, the following argument replacements would be in effect before and after a shift command:

Before Shift

After Shift

%0 = batchname

%0 = xxx

%1 = xxx

%1 = yyy

%2 = yyy

%2 = zzz

%3 = zzz

%3 = (blank)

%4 = (blank)

%4 = (blank)


This lets a batch file repeatedly process the item named by %1 and shift until %1 is blank. To process a variable number of command-line arguments, use the shift command to delete arguments until they're all gone, as in this example:

 @echo off if "%1" == "" (     rem if %1 is blank there were no arguments. Show how to use this batch     echo Usage: %0 filename ...     exit /b ) :again rem if %1 is blank, we are finished if not "%1" == "" (     echo Processing file %1...     rem  ... do something with file %1 here  rem - shift the arguments and examine %1 again     shift     goto again ) 

If you want to have the program process a default file if none is specified on the command line, you can use this variation of the pattern:

 @echo off if "%1" == "" (     rem - no file was specified - process the default file "test.for"     call :process test.for ) else (     rem - process each of the named files :again     rem if %1 is blank, we are finished     if not "%1" == "" (         call :process %1         rem - shift the arguments and examine %1 again         shift         goto again     ) ) exit /b :process echo Processing file %1... . . . 

In this version, if no arguments are specified on the command line, the script will process a default filein this case test.for . Otherwise, it will process all files named on the command line. This version of the pattern uses batch file subroutines, which are discussed later in this chapter under "Using Batch Files Subroutines."

The extended version of the shift command, shift / n , lets you start shifting at argument number n , leaving the lower-numbered arguments alone. The following illustrates what shift /2 does in a batch file run with the command "batchname xxx yyy zzz" :

Before Shift

After Shift

%0 = batchname

%0 = batchname

%1 = xxx

%1 = xxx

%2 = yyy

%2 = zzz

%3 = zzz

%3 = (blank)

%4 = (blank)

%4 = (blank)


In actual use, you might want to use this feature if you need to have the batch file's name ( %0 ) available throughout the batch file. In this case, you can use shift /1 to shift all the remaining arguments, but keep %0 intact. You may also want to write batch files that take a command line of the form

 batchname  outputfile inputfile inputfile  ... 

with an output filename followed by one or more input files. In this case, you could keep the output filename %1 intact but loop through the input files with shift /2 , using commands like this:

 @echo off rem be sure they gave at least two arguments if "%2" == "" (     echo Usage: %0 outfile infile ...     exit /b ) rem collect all input files into SORT.TMP if exist sort.tmp del sort.tmp :again if not "%2" == "" (     echo ...Collecting data from %2     type %2 >>sort.tmp     shift /2     goto again ) rem sort SORT.TMP into first file named on command line echo ...Sorting to create %1 sort sort.tmp /O %1 del sort.tmp 

Working with Environment Variables

Although environment variables were initially designed to hold system-configuration information such as the search path, they are also the "working" variables for batch files. You can use them to store filenames, option settings, user input from prompts, or any other information you need to store in a batch program. The set command is used to set and modify environment variables.

However, you should know that, by default, changes to environment variables made in a batch file persist when the batch file finishes because the variables "belong" to the copy of cmd that manages the Command Prompt window and any batch files in it. This is great when you want to use a batch file to modify the search path so that you can run programs from some nonstandard directory. However, it's a real problem if your batch file assumes that any variables it uses are undefined (empty) before the batch file starts. Here's a disaster waiting to happen:

 @echo off set /p answer=Do you want to erase the input files at the end (Y/N)? if /i "%answer:~,1%" EQU "Y" set cleanup=YES ...  more commands here  ...  then, at the end,  if "%cleanup%" == "YES" (     rem they wanted the input files to be erased     del c:\input\*.dat ) 

If you respond to the prompt with Y , the environment variable cleanup will be set to YES , and the files will be erased. However, the next time you run the batch file, cleanup will still be set to YES , and the files will be erased no matter how you answer the question. Of course, the problem can be solved by adding the statement

 set cleanup= 

at the beginning of the batch file. In fact, good programming practice requires you to do so in any case (you should always initialize variables before using them), but the point is still important: Environment variables are "sticky."

In the old DOS days, a batch file program would usually add set statements to the end of batch files to delete any environment variables used by the program. However, cmd provides an easier method of cleaning up.

If you plan on using environment variables as working variables for a batch file, you can use the setlocal command to make any changes to the variables "local" to the batch file. At the end of the batch file, or if you use an endlocal command, the environment will be restored to its original state at the time of the setlocal command. It would be prudent to put setlocal at the beginning of any batch file that does not require its environment changes to persist outside the batch file itself.

Environment Variable Editing

As with the old command.com , in any command, strings of the form %var% are replaced with the value of the environment variable named var . One of cmd's extensions is to let you modify the environment variable content as it is being extracted. Whereas the edits for command-line arguments are focused around filename manipulation, the edits for environment variables are designed to let you extract substrings.

The following types of expressions can be used:

Expression

Result

%name:~ n %

Skips the first n letters and returns the rest

%name:~ n,m %

Skips n letters and returns the next m

%name:~, m %

First (leftmost) m letters

%name:~-m%

Last (rightmost) m letters


Using the environment variable var=ABCDEFG , here are some examples:

Command

Prints

echo %var%

ABCDEFG

echo %var:~2%

CDEFG

echo %var:~2,3%

CDE

echo %var:~,3%

ABC

echo %var:~-3%

EFG


Expressions of the form %name: str1 = str2 % replace every occurrence of the string str1 with str2 . str2 can be blank to delete all occurrences of str1 . You can start str1 with an asterisk (*) , which makes cmd replace all characters up to and including str1 .

Using the environment variable var=ABC;DEF;GHI , here are some examples:

Command

Prints

echo %var:;= %

ABC DEF GHI

echo %var:;=%

ABCDEFGHI

echo %var:*DEF=123%

123;GHI


The first example listed is particularly useful if you want to use the PATH list in a for loop; for wants to see file or folder names separated by spaces, whereas PATH separates them with semicolons. I'll discuss this in more detail later on.

Processing Multiple Items with the for Command

You'll often want to write batch files that process "all" of a certain type of file. Command-line programs can deal with filename wildcards: For example, you can type delete *.dat to delete all files whose names end with .dat . In batch files, you can accomplish this sort of thing with the for loop.

Note

If you have a UNIX background, the need for special statements to deal with wildcards may seem confusing at first. On UNIX and Linux systems, the command shell expands all command-line arguments with wildcards into a list of names before it starts up the command, so to the command it appears that the user typed out all of the names. This is called globbing . On DOS and Windows, the shell doesn't do this. When command-line arguments contain wildcard characters, it's up to the command or batch file to expand the name into a list of filenames.


The basic version of the for command scans through a set or list of names and runs a command once for each. The format for batch files is

 for %%x in (  set of names  ) do  command  

where set of names is a list of words separated by spaces. The for command executes command once for each item it finds in the set. At each iteration, variable x contains the current name, and any occurrences of %%x in the command are replaced by the current value of x . You can choose any alphabetic letter for the variable name. Also, upper- and lowercase matters, meaning a and A are different to the for command.

Note

When you type a for command directly at the command prompt, you only use single percent signs. In a batch file, you must double them up. Otherwise, they confuse cmd because they look sort of like command-line arguments. cmd could have been written to know the difference, but it wasn't, so we're stuck with this.


For example, the command

 for %%x in (a b c d) do echo %%x 

prints four lines: a , b , c , and d . What makes for especially useful is that if any item in the set contains the wildcard characters ? or * , for will assume that the item is a filename and will replace the item with any matching filenames. The command

 for %%x in (*.tmp *.out *.dbg) do delete %%x 

will delete any occurrences of files ending with .tmp , .out , or .dbg in the current directory. If no such files exist, the command will turn into

 for %%x in () do delete %%x 

which is fine...it does nothing. To get the same "silent" result when specifying the wildcards directly in the delete command, you would have to enter

 if exist *.tmp delete *tmp if exist *.out delete *.out if exist *.dbg delete *.dbg 

because delete complains if it can't find any files to erase.

As another example, the command

[View full width]
 
[View full width]
for %%F in ("%ALLUSERSPROFILE%\Documents\My Faxes\Received Faxes\*.tif") do echo %%~nF: received %%~tF

prints a list of all faxes received from the Windows Fax service and the time they were received.

Note

If you use variable substitution edits, choose as your for variable a letter that you don't need to use as one of the editing letters. for stops looking at the editing expression when it hits the for variable letter. For instance, in the example, if I had needed to use the ~f editing function, I would have had to choose another variable letter for the for loop.


The extended for command lets you scan for directories, recurse into subdirectories, and several other useful things that you can't do any other way.

Using Multiple Commands in a for Loop

cmd lets you use multiple command lines after a for loop. This makes the Windows XP for command much more powerful than the old DOS version. In cases where you would have had to call a batch file subroutine in the past, you can now use parentheses to perform complex operations.

For example, this batch file examines a directory full of Windows bitmap (BMP) files and makes sure that there is a corresponding GIF file in another directory; if the GIF file doesn't exist, it uses an image-conversion utility to create one:

 @echo off setlocal echo Searching for new .BMP files... for %%F in (c:\incoming\*.bmp) do (     rem output file is input file name with extension .GIF     set outfile=c:\outgoing\%%~nF.gif     if not exist %outfile% (          echo ...Creating %outfile%          imgcnv -gif %%F %outfile%     ) ) 

Therefore, every time you run this batch file, it makes sure there is a converted GIF file in the \outgoing folder for every BMP file in the \incoming folder. This sample script uses several of the techniques we've discussed in this chapter:

  • A setlocal statement keeps environment variable changes in the batch file from persisting after the batch is finished.

  • The for loop and if command use parentheses to group several statements.

  • The environment variable outfile is used as a "working" variable.

  • The batch file uses echo statements to let you know what it's doing as it works.

A batch file like this can make short work of maintaining large sets of files. You might accidentally overlook a new file if you were trying to manage something like this manually, but the batch file won't.

As a final example, the following handy batch file tells you what file is actually used when you type a command by name. I call this program which.bat , and when I want to know what program is run by, say, the ping command, I type the following:

 which ping 

The batch file searches the current folder, and then every folder in the PATH list. In each folder, it looks for a specific file, if you typed a specific extension with the command name, or it tries all the extensions in the PATHEXT list, which contains .EXE , .COM , .BAT , and the other usual suspects :

 @echo off if "%1" == "" (     echo Usage: which command     echo Locates the file run when you type 'command'.     exit /b ) for %%d in (. %path%) do (     if "%~x1" == "" (         rem the user didn't type an extension so use the PATHEXT list         for %%e in (%pathext%) do (             if exist %%d\%1%%e (                 echo %%d\%1%%e                 exit /b             )         )     ) else (         rem the user typed a specific extension, so look only for that         if exist %%d\%1 (             echo %%d\%1             exit /b         )     ) ) echo No file for %1 was found 

As you can see, the for command lets you write powerful, useful programs that can save you time and prevent errors, and the syntax is cryptic enough to please even a Perl programmer.

Delayed Expansion

Environment variables and command-line arguments marked with % are replaced with their corresponding values when cmd reads each command line. However, when you're writing for loops and compound if statements, this can cause some unexpected results.

For example, you might want to run a secondary batch file repeatedly with several files, with the first file handled differently, like this:

 call anotherbatch firstfile.txt FIRST call anotherbatch secondfile.txt MORE call anotherbatch xfiles.txt MORE 

You might want to do this so that the first call will create a new output file, while each subsequent call will add on to the existing file.

You might be tempted to automate this process with the for command, using commands like this:

 set isfirst=FIRST for %%f in (*.txt) do (     call anotherbatch %%f %isfirst%     set isfirst=MORE ) 

The idea here is that the second argument to anotherbatch will be FIRST for the first file and MORE for all subsequent files. However, this will not work. cmd will replace %isfirst% with its definition MORE when it first encounters the for statement. When cmd has finished processing % signs, the command will look like this:

 set isfirst=FIRST for %%f in (*.txt) do (     call anotherbatch %%f FIRST     set isfirst=MORE ) 

Because FIRST is substituted before the for loop starts running, anotherbatch will not see the value of isfirst change, and the result will be

 call anotherbatch firstfile.txt FIRST call anotherbatch secondfile.txt FIRST call anotherbatch xfiles.txt FIRST 

which is not at all what you wanted.

There is a way to fix this: Delayed expansion lets you specify environment variables with exclamation points rather than percent signs, as an indication that they are to be expanded only when cmd actually intends to really execute the command. Because ! has not traditionally been a special character, this feature is disabled by default. To enable delayed expansion, specify /V:ON on the cmd command line or use SETLOCAL to enable this feature inside the batch file. The statements

 setlocal enabledelayedexpansion set isfirst=FIRST for %%f in (*.txt) do (     call anotherbatch %%f !isfirst!     set isfirst=MORE ) 

will work correctly.

Although delayed expansion is disabled by default in Windows XP, you can change the default setting through the Registry key HKLM\Software\Microsoft\Command Processor\EnableExtensions . If this DWORD value is present and set to , command extensions are disabled by default. Any nonzero value for EnableExtensions enables them.

Another place delayed expansion is useful is to collect information into an environment variable in a for list. The following batch file adds c:\mystuff and every folder under it to an environment variable named dirs :

 setlocal ENABLEDELAYEDEXPANSION set dirs= for /R c:\mystuff %%d in (.) do set dirs=!dirs!;%%d 

The for statement recursively visits every folder, starting in c:\mystuff , and %%d takes on the name of each folder in turn. The set statement adds each directory name to the end of the dirs variable.

Using Batch Files Subroutines

The cmd shell lets you write batch file subroutines using the call command. Although the new capability to group statements with parentheses makes batch file subroutines somewhat less necessary than they were in the past, the subroutine is still an important tool in batch file programming.

For example, in a task that involves processing a whole list of files, you might write a batch file subroutine to perform all the steps necessary to process one file. Then, you can call this subroutine once for each file you need to process.

In the old days of command.com , batch file subroutines had to be placed in separate .BAT files. You can still do this, but with cmd, you can also place subroutines in the same file as the main batch file program. The structure looks like this:

 @echo off rem MAIN BATCH FILE PROGRAM ------------------------ rem call subroutine "onefile" for each file to be processed: cd \input for %%f in (*.dat) do call :onefile %%f  subroutine called here rem main program must end with exit /b or goto :EOF exit /b rem SUBROUTINE "ONEFILE" --------------------------- :onefile echo Processing file %1... echo ...  commands go here  ... exit /b 

The call command followed by a colon and a label name tells cmd to continue processing at the label. Any items placed on the call command after the label are arguments passed to the subroutine, which can access them with %1 , %2 , and so on. The original command-line arguments to the batch file are hidden while the call is in effect.

Processing returns to the command after the call when the subroutine encounters any of these conditions:

  • The end of the file is reached.

  • The subroutine deliberately jumps to the end of the file with the command goto :EOF .

  • The subroutine executes the command exit /b .

Normally, any of these conditions would indicate the end of the batch file, and cmd would return to the command prompt. After call , however, these conditions end the subroutine, and the batch file continues.

Caution

You must be sure to make the main part of the batch file stop before it runs into the first subroutine. In other scripting languages such as VBScript, the end of the "main program" is unmistakable, but in the batch file language it is not. You must use goto :EOF or exit /B before the first subroutine's label; otherwise, cmd will plow through and run the subroutine's commands again.


Tip

When I have a bunch of files that I have to process in some way, I often don't like to use the for command. Instead I use the command dir /b >filename.bat to create the beginnings of the batch file. I edit the batch file with Notepad and put call :process before each filename. I put @echo off at the top. At the bottom, I put the lines

 goto :EOF :process     echo Processing file %s...     ...  (commands go here)  

The reason I go to this trouble is that I can manually delete from the list any files that I want to skipyou can't do that with for !. And if I have to interrupt the procedure before it finishes, I can delete the call lines for any files that had already been processed, or use a goto to skip over them, before running the batch file again.


Prompting for Input

If your batch file has to print a message you definitely don't want the users to miss , use the pause statement to make the batch file sit and wait until they've read the message and acknowledged it. Here's an example:

 echo The blatfizz command failed. This means that the world as echo we know it is about to end, or, that your input file needs to echo be corrected. pause exit /b 

If you want to ask a user whether to proceed after a mishap, or if you want the batch file to prompt for input filenames or other data, you can use the new extended set /p command. set /p reads a user's response into an environment variable, where it can be tested or used as a command argument. Here's an example:

 :again    echo The input file INPUT.DAT does not exist    set /p answer=Do you want to create it now (Y/N)?    if /i "%answer:~,1%" EQU "Y" goto editit    if /i "%answer:~,1%" EQU "N" exit /b    echo Please type Y for Yes or N for No    goto again 

These commands ask the user to type a response, examine the leftmost letter with %answer:,1% , and take the appropriate action only if the user types a valid response. To prompt a user for a yes/no answer, use a series of commands following this pattern:

 :again    echo  If the question is long or requires an explanation,  echo  use echo commands to display text before the question.  set /p answer=  Ask the question here  (Y/N)?    if /i "%answer:~,1%" EQU "Y"  command to execute for Yes  if /i "%answer:~,1%" EQU "N"  command to execute for No  echo Please type Y for Yes or N for No    goto again 

Put a single space after the question mark on the set /p command. If you use this pattern more than once in the same batch file, be sure to use a different label for each one. I used again in this version, but you can use any word as the label.

If the work that has to be done given either the Yes or No answer is more than a few commands (placed in parentheses), you can use be a goto :label command to continue execution in another part of the batch file.

You can modify this pattern to create a menu. You can write a prompt like this:

 echo Options: [A]dd, [D]elete, [P]rint, [Q]uit, [H]elp" set /p answer=Enter selection: 

In this example, instead of comparing the response to the letters Y and N, you would compare it to A, D, P, Q, and H.




Upgrading and Repairing Microsoft Windows
Upgrading and Repairing Microsoft Windows (2nd Edition)
ISBN: 0789736950
EAN: 2147483647
Year: 2005
Pages: 128

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net