Windows Shell Scripting

Frustration can occur when a script works correctly for some time, then seems to fail for no apparent reason. Often, the problem relates to paths. When you execute a script, you're often relying on the accuracy of several paths, such as the path to the script, the path to the log that the script creates, and the paths to the utilities that the script uses.

Problems with paths can occur for many reasons. Here are three common scenarios.

Scenario 1. Good practice dictates that you create and test a script on a development server so that you don't put your production servers at risk. However, if your production servers have different configurations from the development server, the script will fail on the production servers unless you modify the script's paths accordingly.

Scenario 2. Many scripts use utilities from third parties or resource kits, such as the Microsoft Windows 2000 Server Resource Kit or the Microsoft Windows NT Server 4.0 Resource Kit. Two common practices are to hard-code the location of a utility or use an environment variable (e.g., %NTRES- KIT%) to refer to that location. However, if someone moves a resource kit to a new folder or changes the name of the existing folder but doesn't tell you, that person has unknowingly caused the failure of all the scripts that contain paths to that resource kit's utilities.

Scenario 3. If you store your script and the utility it uses on one server, you use a path such as

C:\tools\contig.exe C:\*.*

to point to the utility. However, if you run the script on a remote machine, the script fails because the C:\tools\ contig.exe file doesn't exist on that computer—the file exists only on the server. You would need to set up a share point and change the code to point to the Universal Naming Convention (UNC) path. For example, you could use code such as

\\server1\tools\contig.exe C:\*.*

If you regularly use a script on both local and remote machines, constantly revising the utility's path can get cumbersome and lead to errors.

In a perfect world, you could deploy your scripts on any machine without having to revise their paths. With some creative coding, you can get a little closer to this perfect world. In the script MakeContig.bat, I include code that automatically determines the path to the utility that the script uses. In addition, MakeContig.bat automatically determines the folder in which you placed the script and creates a log inside that folder, regardless of its location. MakeContig.bat also includes a short routine that lets you quickly get the script ready for debugging in case problems do arise.

When you launch MakeContig.bat without an argument, the script runs in default mode (i.e., with the @EchoOff command and Exit commands operational). If you launch the script with any other argument, the routine provides onscreen instructions about the command syntax to use. You can add the debugging routine to any script by following the instructions in the routine's comments.

Solving Path Problems
I wrote MakeContig.bat to reduce the number of fragmented files on servers. It uses Sysinternals' Contig command-line utility. When you run the script for the first time, the script finds the utility and records its location in an NTFS data stream called location.txt. (If you're unfamiliar with data streams, see "How to Take Advantage of the Hidden Streams in Your Scripts," November 2000.) Each time the script runs, it determines whether the utility exists at the location stored in the data stream. If someone has moved the utility, the script tries to find it. As Listing 1 shows, the script first checks the folder in which the script resides, even if that folder is on a remote server. If the script locates the utility, the script records the new location and runs the utility. If the script doesn't find the utility, the script looks in the NTResKit folder, as callout A in Listing 1 shows. The %NT- RESKIT% environment variable exists only on NT 4.0 machines with the NT resource kit installed. If the OS is Win2K, this step returns a False value. If the utility isn't in the NT- ResKit folder, the script searches the local drives that have administrative shares associated with them. If the utility is still missing, the script issues a warning message that states it was unable to find the utility. In this situation, someone might have inadvertently deleted the utility.

Creating the Log
Many scripts output their results to a log. If the folder structure changes, the log creation and updates can fail. To avoid this problem, MakeContig.bat finds the folder in which the script resides (even if the script resides on a remote server) and places the log in that folder.

Listing 2 contains the code that finds the location of the script's folder. First, the script sets the ScriptName variable to %0, an argument-holding variable that represents the script's name if you run the script from the same directory in which you placed the script. (For more information about this special type of environment variable, see "Shell Scripting 101, Lesson 3,", InstantDoc ID 20142.) Win2K can have problems if the ScriptName variable includes quotes, so the line at callout A in Listing 2 removes any quotes that exist in the script's name.

Next, the script calls the Initialize routine. Notice that the call includes the %0 variable as a parameter. This parameter lets the script substitute batch parameters to obtain the script's path. The script then sets the path to the ScriptPath variable and displays the location in which the script will create the log.

Debugging the Script
MakeContig.bat starts with the @Echo Off command to eliminate the echo of each command to the command-shell window, thereby reducing screen output. The script ends with the Exit command, which closes the command-shell window after the script executes. Although you want to use these commands when a script is running properly, they're undesirable when the script is malfunctioning. To troubleshoot problems, you need to comment out or remove the @Echo Off command so that you can observe each line as it runs to determine the problem. (For information about how to comment out commands, see "Shell Scripting 101, Lesson 1," http://www, InstantDoc ID 16355.) Similarly, you need to comment out or remove the Exit command. Otherwise, when the command-shell window closes, you can't view the onscreen feedback that might reveal why the script is failing.

MakeContig.bat contains a short routine that lets you easily disable the @Echo Off and Exit commands in the script, thereby making debugging the script easier. Listing 3 shows this routine. When you want to debug MakeContig.bat, you use the command

MakeContig.bat Debug

to launch the script. Each command will then echo to the command-shell window, and the command-shell window will stay open after the script executes. If you want to pause the script for onscreen diagnosis during a debug run, you can add the line


at the points in the script at which you want to pause.

Using MakeContig.bat
MakeContig.bat is easy to use. Here are the steps to get the script to work in your environment:

  1. Download MakeContig.bat from the Code Library on the Windows Scripting Solutions Web site to your server or workstation. The script must reside on an NTFS partition to take advantage of the NTFS data-stream capability.
  2. Download the Contig utility from Sysinternals (http://www.sysinternals .com/ntw2k/utilities.shtml) to your server or workstation. The utility doesn't need to be on an NTFS partition. However, if you want to run MakeContig.bat and Contig from a central server, place the script and the utility in the same folder on the server.
  3. Determine and configure the switch options you want Contig to use. The switch options are
  • -a (analyze mode, in which the utility analyzes file fragmentation)
  • -s (recursive mode, in which the utility recursively processes subdirectories when you include wildcards in a filename)
  • -v (verbose mode, in which Contig details all information about the file defrag operations that it performed)
  • -q (quiet mode, in which Contig reports only summary information about the file defrag operations that it performed)

I set the script to use the -a switch. This switch is the best one to use to initially test the script because of its short runtime. After initially testing the script, you can use any combination of switches. For example, to analyze the level of file fragmentation, you use the -s and -a options. To make your files contiguous, you can use the -s and -q switches. For more information about these switches, see Contig's usage instructions, which you access by typing the command

contig -?

To change the switch, you need to modify the line

(Set Switches=-a)

in the script.

  • The log's default name is servername-contiglog.txt, where servername is the name of your server. You can change the second part of the name (i.e., contiglog), if you want. In the line
  • Set LogFileName=contiglog.txt

    replace contiglog.txt with your name for the log. This step is optional.

  • You can change the local drives on which to search for and run Contig, if necessary. By default, the script works with the C, D, E, F, G, H, and I drives, if those drives are associated with administrative shares (e.g., C$, D$). This approach is a convenient way to determine whether a drive exists and isn't a network drive or CD-ROM drive. (If you've removed the administrative shares on your machines for security reasons, you need to modify the code to test the existence of drives by another means.) To change the drives, you need to customize the lines
  • (Set SearchDrives=C D E F G H I)
    (Set ContigTargets=C D E F G H I)

    in the script.

    Say Good-Bye to Path Problems
    Path problems can take time to diagnose. In addition, every time you edit a tested script, you can inadvertently introduce typos or other errors that can cause it to fail. With the code I've shown you, you can avoid path problems entirely.