This section deals with the use of intelligence in command scripting. If we are to use command scripts for exhaustive repetitive processing tasks, we must make use of some of the more advanced features of the Windows command interface. We will learn how to use comparative and loop functions to do more complex tasks. We will also learn how to extract specific data from external utilities for processing and methods for formatting the output to be more presentable.
It should be noted that not all the commands and methods shown here are supported by versions older than Windows 2000. It would also be advisable to visit the ‘Essential Utilites‘ page to get an idea of the type of utilities that are available to us.
Comparative (or conditional) functions in the Windows command context are provided by the IF statement. Basic syntax of the IF statement is:
IF [NOT] ERRORLEVEL number command IF [NOT] string1==string2 command IF [NOT] EXIST filename command
IF [NOT] ERRORLEVEL number command
The errorlevel is a system variable that reflects the exit code of the last program to execute. This is typically ‘0’ or ‘1’. Generally, if the exit code is greater than ‘0’, the program execution failed or had errors.
An example of using the IF statement with the errorlevel variable would be:
if errorlevel 1 exit
or, if reverse logic were to be applied
if not errorlevel 0 exit
This is most useful in capturing and dealing with problems in a script.
IF [NOT] string1==string2 command
This is a simple string comparison function. It should be noted that if the strings have (or may have) spaces they should be enclosed in quotes (“), like: –
IF [NOT] “string1″==”string2” command.
Also, if the string contains quotes, use another character like: –
IF [NOT] #string1#==#string2# command.
IF [NOT] EXIST filename command
This is simply a function to determine whether a file exists or not. If the file is not in the current directory the full path must be included. Quotes must also be used if the filename or path has any spaces, like: –
IF EXIST "C:\Program Files\My Utility\hello.exe" goto somewhere
This function can also be used to determine whether a directory exists or not. To do this we use NUL at the end of the path: –
IF NOT EXIST "C:\Program Files\My Utility\NUL" md "C:\Program Files\My Utility"
This would create the ‘My Utility’ directory if it did not exist.
IF Command Extensions
The IF command has a number of useful extensions if Command Extensions are enabled (they generally are by default). These extensions are as follows (from IF /?): –
IF [/I] string1 compare-op string2 command
IF CMDEXTVERSION number command
IF DEFINED variable command
where compare-op may be one of:
EQU – equal
NEQ – not equal
LSS – less than
LEQ – less than or equal
GTR – greater than
GEQ – greater than or equal
The /I switch is very useful as it allows you to do case insensitive comparison on strings.
The CMDEXTVERSION extension is rarely useful.
The DEFINED extension is very useful for determining whether a variable is set. Usually one would use IF “variable” == “”.
The compare-ops are best utilised in numeric comparisons.
For some good examples have a look at my intermediate and advanced scripts.
The FOR command
The FOR command is undoubtedly the most powerful of all the functions in command scripting. It can be used very effectively for repetitive or recursive operations as well as string and data manipulation.
In basic form the FOR command uses a set of filenames, typically specified by wild-cards, for recursive operations. These filenames are passed to the command component as a variable, for example: –
for %a in (*.txt) do echo %date%>>%a
will append a line containing the current date to each .TXT file.
It’s worth noting at this point that the variable syntax is different in batch files. On the command line you would use ‘%a’, in a batch file you need to use ‘%%a’. Variables are also case sensitive.
If Command Extensions are enabled, the following additional forms of the FOR command are supported (from FOR /?): –
FOR /D %variable% IN (set) DO command [command-parameters]
specifies that FOR should query directory names instead of file names.
FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]
walks the directory tree rooted at [drive:]path, executing the FOR statement in each directory of the tree. If no directory specification is specified after /R then the current directory is assumed. If ‘set’ is just a single period (.) character then it will just enumerate the directory tree.
FOR /L %variable IN (start,step,end) DO command [command-parameters]
the ‘set’ is a sequence of numbers from start to end, by step amount. So (1,1,5) would generate the sequence 1 2 3 4 5 and (5,-1,1) would generate the sequence (5 4 3 2 1)
FOR /F [“options”] %variable IN (file-set) DO command [command-parameters]
FOR /F [“options”] %variable IN (“string”) DO command [command-parameters]
FOR /F [“options”] %variable IN (‘command’) DO command [command-parameters]
or, if usebackq option present:
FOR /F [“options”] %variable IN (file-set) DO command [command-parameters]
FOR /F [“options”] %variable IN (‘string’) DO command [command-parameters]
FOR /F [“options”] %variable IN (`command`) DO command [command-parameters]
Of the above options, the /F switch is the most powerful, and this is where we really need to start getting innovative. Because we have the option of specifying a command for input to the FOR loop, we can do many recursive operations without the need to pipe the command output to a file first. A basic example would be: –
for /f %a in ('net view^|find "\\"') do net view %a
This command would simply get a list of shares from all computers in the domain (ones that are in the netbios cache anyway).
Two things should be noted here:
- special characters in the specified command need to be preceded with a carat (^).
- the /F switch without any parameters will only get the first string on each line up to the first whitespace. If you have output that contains spaces you will need to specify a delimiter.
The FOR command becomes a lot more powerful with parameters. The available parameters are: –
- skip – the number of lines to skip at the beginning of the input
- tokens – the fields to extract
- delims – the field delimiters
- usebackq – useful on rare occasions where the input contains quotes (“)
The first parameter is simple, it allows you to skip a given number of lines in the input. An example would be ‘skip=2’ for input from the FIND command.
The second parameter allows us to specify which fields of data we want to work with in delimited input. Generally this would be comma or tab separated input. An example of this would be the exported data from an address book where fields are as follows: –
If we only want the full name and contact details then we would use: –
for /f "delims=3,4,6,12 delims=," %a in (data.csv) do echo %a,%b,%c,%d >>report.csv
The third parameter allows us to specify one or more delimiters. These would usually be a comma or a tab, but I frequently have input that uses the equal sign or a colon. It should be noted that not all characters can be used, one would be the double quote.
These abilities opens up a whole array of new possibilities in what can be done with command scripting. You will see that I use this type of thing in all my more advanced scripts, especially when generating reports.
The other type of loop is a programmatic loop, using the GOTO function. GOTO simply uses location markers to jump to a specific location in the script. This can be a very important and useful function when needing some intelligence, but it also allows us to do loops based on one or more conditions. For example:-
set count=0 :start set /a count=!count!+1 echo This is file !count! >file!count!.txt if !count! LSS 100 goto start echo !count! files have been created.
This will simply create 99 files named file1.txt to file99.txt containing the string ‘This is file x’ where ‘x’ is the number of the file. Not a very useful script but it does demonstrate the loop effectively.
TIP: Notice the ‘SET /A’ function, the /A switch allows you to do simple arithmetic operations on variables. This has proved to be very useful in many of my scripts.
One of the more difficult things in scripting is manipulating the data that is being processed. This could range from replacing characters or delimiters to altering the data format. For example, if you have a utility that exports data in CSV format(Comma Separated Values for those who don’t know), the expectation is that each field would be separated by a comma. Unfortunately, this is not always the case. The problem is that sometimes the data itself needs to contain a comma. Some software vendors have overcome this problem by enclosing each field in quotes. This way their software will only separate fields where it finds “,”. This doesn’t help us though, because we can only use one character as the delimiter.
Don’t worry though, there is usually a way to do whatever it is you need. In cases like the CSV data above, you would simply need to put some extra intelligence in the code to separate the fields correctly. In this case, you could check each field for the existence of the quote at the beginning or end of the field.
It is for this reason that I prefer to use the TAB character instead of the comma. You are unlikely to find the TAB character in any field from any file or data. In all my years I have yet to find one instance where this has been a problem. So as rule, I use TSV (Tab Separated Values) files whenever I’m working with data.
Note: One other thing that’s also worth knowing – if you use Microsoft® Excel®, you can copy the contents of a spreadsheet and paste it directly into a text editor. The result is always TAB separated.
TIP: If you ever need to work with difficult CSV files, one good option is to use the MTR utility to re-format the file or data. MTR will work on a file or accept piped input. The idea is to use MTR to replace all instances of “,” with the TAB character.
When working with utilities that produce the data you need, always study the output and any options relating to the output. Some utilities will even give you a choice as to whether you want the output in CSV format or not. Generally, you will have to exclude some of the output, like headings or copyright notices or simply unneeded data. One good example would be the SRVINFO utility. This utility displays all sorts of information about your computer, but when it comes to scripting it’s not in a very friendly format. To deal with this we look for attributes of each component that can be used to filter the data to produce only what we want. For instance, if you want the hot-fix information you would pipe the output to FIND with the string of “]: Installed” as the argument. This specific string is only found in entries relating to hot-fixes. If you want only the drive information you would use “$” as the string to look for. In each of these cases you get only the info you want, with fields separated by ‘:’ or spaces. This is what I consider to be ‘predictable output’, where the data is in a consistent format. Each line can be broken down into separate fields and the content used to good effect. To see a good example of this have a look at my SRVSTAT script, which uses a similar utility PSINFO.
Dealing with the different sorts of data effectively takes a bit of innovative thinking and lots of practice. In most cases, success is dependent on your ability to predict the format of the data and how to manipulate it in such a way that it can be used in the script. With this in mind I recommend that you get down to business and practice as much as you can.
While I know that most of the difficulties can be overcome when dealing with files and utilities and the data that they present, there is the odd occasion where it’s just not possible to work with what you have. This is usually due to special characters or unpredictable output. My recommendation here is to find an alternative utility that will produce the same data in a better format.
There are also some system limitations which you may come across. Try to remember that the command prompt in Windows comes from the DOS legacy. Microsoft® has never really put any real power into it. As a result there are limitations on things like memory, environment space etc. I will say though that I have not yet reached any of these limitations.
In the end, what you get out is what you put in. Good luck and happy scripting.