Real-World Shell Scripting
The ability to sort and extract (i.e., process) information from a list is a handy scripting technique. By sorting and extracting information from a list, you can reduce the number of input and temporary files that a script must use to complete its tasks, thereby reducing the script's complexity and execution time. This month, I want to show you some advanced For command techniques for sorting through a list and picking out specific lines of information.
ServerStatus.bat, which you can find in the Code Library on the Windows Scripting Solutions Web site (http://www.winscriptingsolutions.com), began life as a quick solution to server-uptime problems at a client site. Originally, the script used three input files to test servers for Ping response, services, and share availability. The script then used seven temporary files, which it created and deleted as it ran, to sort the test data and organize it into a report. I quickly realized that the script was too complicated for easily testing the existence of a new share or pinging a server. Consequently, I undertook the job of combining the three input files into one file and eliminating all the temporary files.
The single input file that ServerStatus.bat uses contains three types of test input—input for the ping test, input for the service test, and input for the share test. For the ping-test input, you create entries that specify the servers you want to ping. For each server, you provide the server name and IP address, such as
For the service-test input, you create entries that specify the services you want to test. The Netsvc utility, which you can obtain from the Microsoft Windows 2000 Server Resource Kit or the Microsoft Windows NT Server 4.0 Resource Kit, will use this input to perform service-status tests. For each service, you provide the name of the server on which the service runs, followed by the service's friendly name (i.e., the name that appears in displays) and the actual service name (i.e., the name the OS uses internally). For example, a service-input entry might look like
For the share-test input, you create entries that specify the shares you want to test. For each share, you provide the name of the server on which the share exists, followed by the share's friendly name and the actual share path. Here's a sample share-input entry:
You combine all the entries into a comma-separated value (CSV) file named ServerStatusInput.csv. The contents of this input file should resemble the contents that Figure 1 shows. You might notice that the sample ServerStatusInput.csv file doesn't follow a strict order according to server or type of test input. You would probably see this type of variance in the real world as different administrators add test targets over time. In a production setting of 50 servers, for example, the number of individual items you're checking for can easily grow beyond 200, and items can be added out of order. Fortunately, you don't need to group the entries by server or type of test input in ServerStatusInput.csv. A For command in ServerStatus.bat sorts the entries.
Finding Unique Items
Ideally, on the output side, you'd like to see test results grouped by server and have all like items displayed together in alphabetical order. So, the first step toward that goal is to sort the entries in ServerStatusInput.csv by server names. To do so, you embed the Sort command in a For command, as Listing 1 shows. The Sort command reorders the server names that the script reads from the input file. If a name is the same as the previously read name, the code moves on to the next name. If the name is unique, the script proceeds to the code under the :Unique label. As Listing 2 shows, the Unique module tests the items listed in the ServerStatusInput.csv file.
Findstr Pattern Matching
After you have a unique server name from your input list, you need to extract the information that tells you which server to ping, which service to test, and which share to test. Many scriptwriters are unaware that they can use the Findstr command to perform elementary pattern matching in shell scripting. Take, for example, the code at callout A in Listing 2. This code uses the Findstr command to find the entries that contain the ping-test input. The \[0-9\]* syntax denotes the occurrence of any number of digits separated by "\.", which is a literal period. The %TestObj% variable specifies the input file (i.e., ServerStatusInput.csv).
The share-input entries contain a server path to the share, so to obtain the shares for which you want to test for existence, you can use the command sequence that callout C in Listing 2 highlights. The Sort command in the command sequence puts the shares in alphabetical order. Note that the code uses the pipe symbol (|) to chain together the Findstr and Sort commands. You need to use the caret symbol (^) to escape out the pipe symbol if you use the pipe symbol inside a For command.
Locating the service-input entries is a little more difficult than finding the other types of entries because the service-input entries don't have any distinguishing characters or character sequences for which to search. The simple way to locate these entries is to use the pattern matching from the previous example, except use the /V option with Findstr to locate items that don't match the pattern, as the code at callout B in Listing 2 shows. By eliminating the ping-input and share-input entries, you're left with the service-input entries.
For Command Loop Syntax Options
One of the For command tricks that I use frequently is to set an environment variable, then perform other operations on that variable by using a label to call another section of code. You can write the code to accomplish this task several ways. One way is to use the ampersand character (&) to join the commands, as follows:
%%i in ('Findstr /I
"\\%Server%" "%TestObj%" ^|
Sort') Do (Set Friendly=%%i)
& (Set SharePth=%%j) &
Another way is to place a carriage return after the starting parenthesis, then put each item on a separate line followed by a closing parenthesis, as the code at callouts A, B, and C in Listing 2 shows. For even easier reading, I used tabs to indent the code inside the For command loop. If you use the latter technique, be sure to place a carriage return after each variable or you might accidentally add a space to a variable, thereby causing problems later. You need to decide which option is easiest to follow. Many scriptwriters find the second option similar to other programming languages' syntax and therefore easier to read.
In the second part of the script, you need to process command output similarly to the way you processed the input list. For example, the code that Listing 3 shows checks the Ping response. The added Else comment clarifies that if the script doesn't find the string Reply in the Ping command output, the assumption is that the machine isn't reachable.
Get It Working
ServerStatus.bat runs on Windows XP and Win2K (any service pack level). To get the script working in your environment, follow these steps:
- Create the ServerStatusInput.csv file. Use the format that Figure 1 shows. Note that leading and trailing spaces can cause the ServerStatus.bat script to fail.
- Verify that you have adequate permissions on servers and shares that you plan to test.
- Configure the ServerStatus.bat script with the location of your CSV input file, the CSV report file, and the Netsvc utility. Listing 4 shows the code that you need to configure.
- Run the script and review the report file.
You can use the pattern-matching and Sort routines that the script contains on a variety of other files (e.g., Web server log files, Event Viewer logs, exported Performance Monitor logs) that you probably encounter on a daily basis.
The ability to quickly sort and locate information can help make your administrative tasks faster and more accurate. Also, after you introduce the script into your environment, you'll find that you can perform other tasks while your script is processing the list and locating the data you need.