I joke that I have only one script, which I keep modifying to perform different tasks. That claim is a bit of a stretch, but it is true that most of the Windows command-shell scripts you'll write will share many sections of common code. Writing code in modular sections lets you more easily reuse the code in future scripts (see the Web-exclusive sidebar "Modularizing Your Shell Script Code," http://www.windowsitpro.com/windowsscripting, InstantDoc ID 44197, for more discussion of code modularization). However, if you update a code module, you then must find and modify all the scripts that use that module. Wouldn't it be great if you could store modules for reuse and automatically apply changes you make to one module to all existing scripts that use the module and execute on the same node? You can, by using a new modular-coding technique that will simplify your command-shell script development and significantly shorten your scripts. That new technique uses environmental variables to store and call code modules.
Hold the Code
Listing 1 shows some pseudo-code (i.e., English sentence code) for a simple Ping script. If you've written scripts that perform such operations, you're probably already visualizing the For commands and other code needed to create this script. And if you're used to writing code in modules, you're probably visualizing four or five code modules that will do the job.
I imagine you're familiar with environment variables that store system or user information. You might have created local environment variables in your scripts, to hold values that your code created or accessed. Imagine if you could use such variables in a new way—to hold script code modules.
First, take a look at Listing 2, which shows completed shorthand code to run the script that Listing 1 lays out. Yes, that's right—only 11 short lines of code (and I could have tightened the code further if I'd combined functions). Some lines of shorthand code, such as the _GetDateForFileName code snippet, look similar to regular script code; other lines look quite different.
Now, look at the code in Listing 3, which changes the script to query an organizational unit (OU) for a list of computers instead of using a static list like the code in Listing 2 does. Because the code is modularized, this change involves only two lines of code from the previous script (the code at callout A and callout B in Listing 2 becomes the code at callout A and callout B in Listing 3).
Table 1 shows the actual code that's stored in the environment variable code lines in these two listings. You can see how simple it is to modify a script using modularization and environment variable shorthand code. By adding more code modules to my environment variable code library, I can quickly and easily reuse code.
Get It Together
Here are some basic principles that I abide by when writing environment variable scripts. These guidelines are valuable in any script but are particularly helpful in shorthand scripts.
- Place all modifications, or Set commands, at the top of the script.
- Minimize such modifications as much as possible.
- Code the script to locate itself and work, even when there are spaces in the script's path. Then, you can put input and log files in the script directory without using additional Set commands in the script.
- Use universal environment variables so that code modules can easily interface with one another.
The easiest way to make environment variable code entries is to click Environment Variables on the Advanced tab of the Control Panel System applet (in Windows Server 2003, Windows XP, or Windows 2000). If you want the variable to be accessible only in your profile, enter your variable name and value as a User Variable. However, I prefer to enter the variable as a System Variable so that it will be available in any user context and will work even when no users are logged on so that I can run scripts as Scheduled Tasks. Any environment variable code snippets that you create will take effect only in a new window or when you double-click the code to open it in a new session. Be aware that the Setx utility (from the Microsoft Windows 2000 Resource Kit), which can set environment variables, doesn't work well setting variables that contain reserved characters, such as those that I use in my sample code. Therefore, using Setx with the samples that I provide could introduce errors into the code.
When you update environment variable code, test it thoroughly before implementing any changes, then test your changes before implementing them across existing scripts. Remember that the next time scripts that use environment variable code run, they'll run with the updated code—so make sure your scripts still work.
If you need to view all your environment variable commands, just type
at a command prompt. You'll get a list of the commands along with all your other environment variables. I suggest you give your environment variable code variables a name prefix, such as an underscore (_), so that they show up together in the variable list and are easy to differentiate from other environment variables.
To use the environment variable script code on a different node, you'll need to set the environment variables on that node. You can dump the code by using the Set command and Findstr to search for the prefix characters you used. For example, for variables that use the underscore as the prefix character, I've used the command
Set | Findstr /B "_">D:\EVsnippets\code.txt
Use single percent signs (%) for your iterators, as you would when running the code from a command prompt. Use the caret character (^) to escape any reserved characters in your code. You'll need to use this character to replace the ampersand (&) in chained commands as well as using it in For commands. See the _ParseInputFile and _PingNodes variable values in Table 1 for examples. Also, place spaces before and after any chained commands by using the ^|^| or ^&^& or ^& character combinations.
If you use a non-built-in tool or command such as Dsquery (which I use in my example) inside your code, either verify that the utility is in the path or specify the full path to the utility. To use a variable inside a For command to specify a filename, use a command such as Type or Echo to extract the variable value. For example, the following shorthand code snippet will succeed because I used the Type command to extract the %InFileName% variable:
For /F "tokens=*" %i in ('Type "%dirloc%\%InFileName%"') do (Set line=%i) ^& (Call :Next)5
Fast and Easy
I tested the code in this article on Windows 2003, XP Service Pack 1 (SP1), and Win2K (both Professional and Server) SP4. You can download the sample scripts EVcommandsPingFromList.bat and EVcommandsPingFromOUquery.bat—as well as EVcode.txt, which contains the five sample environment variable commands—from the Windows Scripting Solutions Web site (enter InstantDoc ID 45147, then click the 45147.zip hotlink).
Now you have a new way to use environment variables to store basic code modules. You can add your own code to the sample code modules that I've included here, then build your own library of environment variable shorthand commands. Reusing code that you've already developed and debugged should make it easier and quicker to create scripts.