Scripting in Windows NT, part 1

In part 1 of this series, Richard Charrington shows you how to create more efficient Windows NT 4.0 scripts.

In this series, I’ll describe some of the techniques, command syntax, and special characters that you can use to make your Windows NT scripts more efficient and easier to write. Along with the standard commands, I’ll describe the command extensions, which are powerful new constructs that were introduced with Windows NT 4.0. These command extensions are switched on by default when you install NT 4. They can be switched off, but I’m not sure why anyone would want to do so. I don’t intend to review and describe all the available commands on the command line—just the ones I’ve found useful in writing scripts. As usual, the text will be interspersed liberally with examples.

In this Daily Drill Down, I’ll cover two important aspects of scripting in Windows NT: special characters and subroutines. I’ll conclude with the if statement.

Special characters
There are a few characters that have special meaning and that can be used to make your code more effective. These special characters aren’t widely documented. I’ll list them first, with a brief description, and then I’ll explain how they’re used. Beware of using any of these characters on the same line after an echo command because they’ll lose their special meaning and will be displayed as normal characters.

&  command separator
Š  pipes the output of the command on the left to the command on the right
&&  equivalent to then, as used in if...then...
ŠŠ  equivalent to else, as used in if...else...
:  can be used in place of REM and looks much neater
()  used to group commands together

The ampersand character is used to separate commands on the same line. Using this character, you can (a) reduce the number of lines in a batch file, (b) make your code look more efficient (although it won’t actually be more efficient), and (c) group certain commands logically to make the code more readable.

Here’s an example:
dir c:\*.bat & echo Directory list of C:\*.bat complete
This command will display all the batch files in the root of drive C and then will display the line
Directory list of C:\*.bat complete
The pipe character is used to pass the output of one command to another. This command is very useful, especially when used with the find command. Here’s an example:
dir c:\*.bat Š find "autoexec.bat"
I’d like to point out a couple of things here. First, the dir command won’t display anything because the output of that command is passed straight to the find command. Second, the find command may not display anything, either, because the command performs a case-sensitive search for the string that follows. Therefore, if the dir command lists files with the first character capitalized or if the filename is stored in uppercase only (or, indeed, if the file doesn’t exist), then find won’t find the string "autoexec.bat" in the output from dir c:\*.bat.

Unfortunately, there’s no switch provided with find that will tell it to conduct a case-insensitive search.

The double ampersand is one of the lesser-known special characters that are available with Windows NT 4.0. It checks the result of the previous command (on the same line), and if the command was successful, it will execute the command that follows.

Here’s an example:
find "Example" Scripting.txt && echo Found text
To make sense of this line, it can be read as If the word Example can be found in the file Scripting.txt, then display "Found text". So, it’s like an if...then... statement with a silent if.

The double pipe is another of the lesser-known special characters. It’s similar to the double ampersand, except that it means else rather than then. Here’s an example:
find "Example" Scripting.txt && echo Text not found
So, it can be read as If the commandfind succeeds else display "Text not found". So, it’s like an if...else... statement with a silent if.

If, like me, you find that the REM statement distracts rather than draws your attention to comments, you’ll like the colon, which can be used to replace it. Remember that it’s really a sort of side effect because the colon normally is used to identify labels in batch files. So, make sure that you put a space after the colon, or make sure that the comment lines can’t be confused with a real label. I tend to combine the colon with another one or two characters (such as ::> or :->) to make it distinctive.

Just like in many programming languages, commands or statements can be grouped together with parentheses. They can be used aesthetically (if you’re interested in helping a person who may have to debug or modify your scripts at some time in the future) or logically.

Here’s an example:
dir c:\testdir ŠŠ (md c:\testdir & echo Something > c:\testdir\testfile)
This example checks for the existence of c:\testdir. If the dir command fails (there is no such directory), then the directory will be created and a file, testfile, will be created in that directory.

Not only do the parentheses emphatically show what this command is supposed to do, but they enforce that structure. Without the parentheses, the above example could be read as If the directory does not exist, then create it. Next, create a file in that directory (regardless of the test, because it is the next command to carry out).

There are usually orders of precedence in these things, and if it looks logical, it should work out that way. However, I prefer not to have to try to remember too much, and using parentheses ensures that it will work the way I want. In addition, you aren’t forcing the next person to decipher your code in order to know the order of precedence or to attempt to work out what your code is trying to achieve.

So, there are some pretty powerful characters. If you use them properly, you may never need to use the ubiquitous if errorlevel 1... after a command to test whether it succeeded or failed; instead, you can have the command, the test, and the command carry out (depending on the result of the test) all on the same line. What you can’t do, unfortunately, is run a command, do something if it failed, or do something else if it succeeded—except, of course, by using the dreaded goto.

So, you could write
dir c:\testdir && goto DirFound
md c:\testdir & echo > c:\testdir\testfile

type c:\testdir\testfile
But you could not write:
dir c:\testdir && type c:\testdir\testfile ŠŠ (md c:\testdir & echo > c:\testdir\testfile)
In such a structure, the command(s) following ŠŠ would be carried out if the type c:\testdir\testfile command failed.

If you’re familiar with writing Windows batch file scripts but you’re new to Windows NT 4.0 scripting, it may surprise you to know that you now have access to subroutines. This access means that you don’t have to write code into a separate batch file and “call” it to create a subroutine structure. All the code can now be contained in a single batch file.

The key to this great step forward is the built-in label :EOF. As you’d expect, this little beauty means end-of-file and is always used with the goto command. No, there are no gosub, sub, subroutine, or function structures. It’s a very simple facility, but it takes a little explaining. I’ll begin with an example:
for %%i in (c:\temp\~*.tmp) do call :DoIt %%I
goto :EOF
echo %1
echo %1 > c:\tempdel.lst
del %1
goto :EOF
There are a number of points to make here. The first is that, if you don’t understand the syntax of the for command, we’ll delve into that subject in part 2 of this series. For now, the for statement above reads For each file in c:\temp that begins with a tilde and ends with .tmp, jump (call) the code beginning with the label:DoIt, passing the name of the file. The :DoIt code gets called for each and every file beginning with tilde and ending with .tmp.

The use of %% is peculiar to batch files. If this command (or something like it) were run on the command line, single percent characters would be used in place of the double-percent characters. As you can see, it’s not the case for all percent characters—only the ones used in the for statement.

The second point to make is that, unlike in a goto statement, the label following the call command begins with a colon, which differentiates the label from a filename. (A filename is what usually follows a call statement.) So, in this case, it’s equivalent to a gosub statement in BASIC. However, in every other respect, the call statement works in the same way as it does when calling another batch file. Parameters can be passed as strings following the label, and they’re accessed in the called code with the parameters %1, %2, etc. It also means that processing returns to the point just after the call once the end of the file has been reached. This point is where goto :EOF comes in. It means exactly what it says—at this point, go to the end of the file. In the code above, it acts just like a return statement in BASIC.

The if statement
I’ve already shown you how you can reduce the need for the if errorlevel 1... statement by using the special characters ŠŠ and &&. However, there are times when you’ll need to use the if statement. First, let’s take a look at the three standard if constructs:
if [NOT] ERRORLEVEL numbercommand
if [NOT] EXIST filename command
if [NOT] string1==string2
The ERRORLEVEL construct checks the result of the previous command. Some commands are written to return an error number that equates to the failed condition of the command. If will run command if the errorlevel returned is equal to or greater than the errorlevel number specified in your statement. Therefore, if you’re checking for more than one errorlevel, you must check for the highest number first. This action is performed as follows:
if ERRORLEVEL 33 goto NoFile
if ERRORLEVEL 10 goto BadFile
if ERRORLEVEL 1 echo Zero length file
goto :EOF

echo File was not found
goto :EOF

echo File was bad
goto :EOF
If the checks were done in reverse order, despite the result of the command somecommand (except success), the result would be Zero length file.

The EXIST construct, as you’d expect, checks for the existence of a file. Since this is a specific file, wildcards are inappropriate. An oft-used statement would be
if NOT EXIST c:\test.txt echo > c:\test.txt
This statement checks for the existence of the file c:\test.txt, and if it doesn’t exist, the statement creates it. It’s simple, but effective.

The == construct compares the value of two strings. Usually, this construct is used to check if a variable is set to a particular value, as follows:
if '%test%'=='ON' goto Test
if NOT '%test%'=='OFF' goto NotSet
echo Test will not be run
goto :EOF
echo Start test
goto :EOF
echo Test variable is not set
goto :EOF
You may wonder why the variable %test% is enclosed in single quotes. Well, remember that the if statement checks two strings. If the variable %test% hasn’t been set, the if statement will result in an error, and the batch file will be aborted. The single quotes ensure that there is something to check. The single quotes can be replaced by any character, and only one needs to be present (for example, %test%x==ONx). The additional character(s) is not enforced; if you know that the variable you’re checking will always be set to some value before it’s tested, you can leave the variable name unadorned.

Three command extension constructs were added to the if statement:
if [NOT] DEFINED variable command
if [NOT} [/I] string1 comparison string2 command
if [NOT] CMDEXTVERSION number command
The first new construct is, in effect, a way around the requirement to add an extra character or characters, as in the previous example. In other words, you could first use the DEFINED construct, then use the == construct. For example:
if NOT DEFINED %test% set test=ON
if %test%==ON goto Test
if NOT %test%==OFF goto NotSet
echo Test will not be run
goto :EOF

echo Start test
goto :EOF

echo Test variable is not set
goto :EOF
The second construct is an extension of the == form. Replacing the == sign are the following comparison operators:
EQU = Equal to
NEQ = Not equal to
LSS = Less than
LEQ = Less than or equal to
GTR = Greater than
GEQ = Greater than or equal to
The optional /I switch makes the comparison case insensitive when it compares strings, and it also can be used in the original == construct. If both strings consist of all numeric digits, a numeric comparison is performed.

Finally, the least useful extension, at this time, is the CMDEXTVERSION construct. It works in the same way as the ERRORLEVEL construct that I described earlier does. As you may have worked out, instead of an errorlevel, this construct checks the version number of the command extensions. The version number started with 1 and will be incremented by 1 whenever significant enhancements are added to the command extensions.

A couple more enhancements were added with the command extensions. %ERRORLEVEL% expands to the current value of ERRORLEVEL, and %CMDLINE% will expand to the original command line passed to CMD.EXE prior to any processing by CMD.EXE. The returned values won’t be correct if you personally assign values to these variable names.

Next time
In part 2 of this series, I’ll discuss the for statement, which has been vastly enhanced with the introduction of command extensions. I’ll conclude with the Replace statement—perhaps one of the least useful commands that are available in Windows NT.

Richard Charrington’s computer career began when he started working with PCs—back when they were known as microcomputers. Starting as a programmer, he worked his way up to the lofty heights of a Windows NT Systems Administrator, and he has done just about everything in between. Richard has been working with Windows since before it had a proper GUI and with Windows NT since it was LANManager. Now a contractor, he has slipped into script writing for Windows NT and has built some very useful auto-admin utilities.

The authors and editors have taken care in preparation of the content contained herein, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.

Editor's Picks

Free Newsletters, In your Inbox