Bring external input into your scripts
After the success of your first simple command-shell script (e.g., for pinging a server), you probably tried to think of ways to easily reuse the script on another server or on a group of servers. The challenge you probably faced was figuring out the best way to include in your script input that's easy to change. If you hard-code the input, you must modify the script each time you want to change the input. For example, if you hard-code the name of the computer on which you plan to perform an operation, you must modify the script each time you want to perform the operation on a different computer. Script modifications are not only inconvenient but also introduce the potential for unintentionally changing the code and causing the script to work improperly. Keeping your script "set in stone" after it's debugged so that it continues to work correctly is preferable.
If you have only a few pieces of information that you need to input into a script, an effective solution is to use command-line arguments to provide that input. After you develop an argument-driven script, you can easily convert it to use other input techniques as requirements evolve. (For information about input alternatives, see the sidebar "Input Options," page 2.) To help you better understand how to use command-line arguments and how to avoid common mistakes, I've put together some FAQs.
One of my favorite utilities requires that I configure about 10 switches and parameters. Every time I run the utility, I need to review the online Help and remember the syntax I used the last time. I finally created a generic script that I modify each time. I've found that I change only three parameters. Can I use command-line arguments with my template script to provide these parameters when I launch the utility?
Creating a script to launch your favorite resource kit or third-party tool is a great way to use command-line arguments—particularly if the tool you use has several complex switches. Most of the time, you probably use the tool with the same switch options. For example, I frequently use the Microsoft Windows NT Server 4.0 Resource Kit's Robocopy utility—a tool that offers numerous switch options—for large data migrations. If you create a script that handles most of the utility's operations, you can save time and ensure the accuracy of the utility's results. Simply write the script with the set options already configured and use command-line arguments to provide the parameters or switches that often change. However, remember that accidentally including or excluding a switch in Robocopy can have devastating results—for example, you might move files you meant to copy, or files might arrive at a destination with lost NTFS permissions.
Listing 1 shows RoboArg.bat, a sample script that runs Robocopy. When you launch this script at the command line, you need to provide two arguments and follow the syntax
RoboArg.bat "C:\sourcefiles" "D:\destinationfiles"
where C:\sourcefiles is the path to the file you want to copy and D:\destinationfiles specifies the location to which you want to copy that file.
In Windows shell scripting, a special group of environment variables exists: the argument-holding variables %0 through %9. You can use these variables to bring input into your script at runtime. The %1 variable represents the first argument you enter on the command line, the %2 variable represents the second argument, and so on. Thus, in RoboArg.bat, %1 and %2 represent the source and destination files, respectively.
RoboArg.bat begins by performing a couple of tests to ensure that you entered the correct number of command-line arguments. For the first test, the script uses the If command to compare the %2 variable's value against an empty string. If a match occurs, the script warns you that you didn't enter enough arguments, then exits. For the second test, the script uses the GTR operator (i.e., the equivalent of the greater than—>—symbol) to determine whether the %3 variable contains a value. You use the GTR operator to compare two values. If the first value is greater than the second value, the comparison evaluates to True, and the rest of the command in that command sequence executes. If the first value isn't greater than the second value, the comparison evaluates to False, so the scripting engine doesn't execute the rest of the command in that sequence. Instead, the scripting engine proceeds to the next line of code. Although the GTR operator is typically used as a math operator, I use it to test for the existence of values. In command-shell scripting, testing whether a variable contains a value is always more reliable than testing whether it contains nothing. If the %3 variable is empty, its contents won't be greater than 1, so the script skips the rest of the command on that line and proceeds to the next line, which launches the Robocopy utility. If the %3 variable isn't empty, the variable's value will be greater than 1, so the script carries out the rest of the command on that line, which is to display the message Too many arguments, then exit. For a glimpse at some other common argument-handling approaches, see the sidebar "Argument-Handling Approaches."
I understand that the %1 and %2 variables represent the first and second arguments. Does the %0 variable represent anything?
Yes, the %0 variable represents the path to the script. To see the %0 variable in action, run the TestVar.bat script, which Listing 2 shows.
The ability to locate the folder that holds the script is helpful. Suppose you want to have the script autolocate other resource files (e.g., utilities, input files, output files) that you've placed in the same folder. You can use the %0 variable combined with a Call command to obtain the location. Then, you can move the script to any server or folder location, and it will run correctly without requiring modifications. Listing 3 shows Test0Var.bat, which contains the Call routine you would use to accomplish this autolocation function.
The presence of the %~dp1 batch-parameter option demonstrates the ability to substitute batch parameters, which is useful in many scripting operations. Table 1 lists a few batch-parameter options. Note that the %~dp1 option is a combination of the %~d1 and %~p1 options, which determine drive letter and path, respectively. For a complete listing of batch-parameter options, run the Call /? command.
Isn't the %CD% environment variable an easier method to determine the location of the .bat file than the %~dp1 batch-parameter option?
The %CD% variable gives you the location from which a script is launched. That location might or might not be the folder in which the script resides.
I know that only nine variables (i.e., %1 through %9) represent arguments in a script. How many characters and arguments can I enter at a command prompt?
The maximum number of characters depends on your OS and the path to your script, which also counts toward the maximum number of characters that can run on the command line. In Windows 2000, that maximum is approximately 2045 characters and spaces. If you plan to process that many characters on the command line, you're going to have a difficult time ensuring that all the entered information is accurate. Also, the command shell doesn't necessarily report errors when you exceed the maximum. If you're using especially long entries or a large number of entries, I recommend parsing an input file.
The answer to the question about maximum number of arguments is that you're limited only by the maximum number of characters and separators (i.e., many more than you'd ever need). Typically, you can access only nine arguments, but you can use the Shift command to make additional arguments available. For details about Shift's syntax, run the Shift /? command.
Listing 4, page 4, shows the TestLotsOfArgs.bat script, which contains sample code for handling an unlimited number of arguments. This code also demonstrates how to detect that no arguments are entered. In this example, I've used the code to ping every server-name argument.
I'm trying to enter an ADsPath string as an argument to launch the DSQuery tool, but it won't work. I'm using MyScript.bat ou=server,dc=sales,dc=mycompany, dc=com. What am I doing wrong?
Although using spaces as argument separators is common, you can use other separators (e.g., commas, semicolons) to break up your arguments. However, if you want to use an ADsPath string as an argument, you need to surround the entry with double quotation marks to prevent the script from interpreting the commas as separators. You might find that after the argument is inside your script, you need to take out the double quotes. To do so, you can use a substitution, as Listing 5 shows. You can use this code—a handy way to remove or change characters—to filter out those double quotes. The basic syntax is %PATH:str1=str2%. For details about variable substitution, run the Set /? command.
If you ran the Call /? command as I described earlier, you might have noticed another method for eliminating double quotes. Listing 6 shows an example of how to use the Call command to filter out double quotes.
Remember, you need to encapsulate any argument that contains spaces or other potential separators in double quotes so that your script considers the argument as a single argument. One common type of script input with which you might encounter spaces is file paths to input, output, or log files. Also, you would need to encapsulate email addresses separated by commas or semicolons.
I have a script that has three arguments. Can I label my arguments so that I can enter them in any order, then test to ensure that the script contains all three arguments and none are missing?
You can make a three-argument script that uses argument labels. For example, I created a second Robocopy script called AdvancedArgumentsForRobo.bat, which Web Listing 1 (http://www.winnetmag.com/windowsscripting, InstantDoc ID 40514) shows, that uses a set number (3) of common arguments, then uses argument labels to bring in the source, destination, and log file location as arguments in any order. The run syntax is
AdvancedArgumentsForRobo.bat /S:"D:\sourcelocation" /D:"\\server\destinationlocation" /L:"D:\logfiles\log.txt".
where /S is the location of the source files you want to copy, /D is the copy destination, and /L is the location of the Robocopy log file. Note that you can specify either a local drive or remote share.
I'd recommend following a few basic principles when you deal with arguments. I've followed these principles in AdvancedArgumentsForRobo.bat.
- If the script operator attempts to run the script without arguments, give the user a Help screen that contains arguments hints.
- Perform a validity check on arguments, if possible. In Web Listing 1, for example, I use an If Exist statement to test whether the source and destination folders exist. Also, if you're using labeled arguments, you might want to verify that no argument is specified twice and that no argument is missing. The code at callout A in Web Listing 1 shows you how to accomplish this verification.
- Permit the user one final bailout point before the operation launches. A bailout point is particularly important if the operation will change, copy, or delete files, user accounts, group memberships, and so on. If the script is performing a read-only or query-only operation, a bailout point isn't crucial, but I still recommend it. If you plan to use a script to perform a scheduled task, you'll want to skip or remove any code that requires user input.
Can I use arguments with shortcuts or in Task Scheduler?
You can specify arguments in shortcuts and Task Scheduler targets, but I recommend adding the arguments after the Shortcut Creation or Task Setup Wizard process. Simply complete the wizard pointing to the .bat file, then manually add the arguments. If the path to the script file contains spaces, be sure to encapsulate the script path—but not the arguments—in quotation marks. If you're using Microsoft's shortcut.exe shortcut-creator tool, you can use the tool's -a switch to specify arguments.
Can you recommend a quick way to use arguments at the command prompt without having to type in the arguments and risk keyboard errors?
I use several techniques to make my command entries more accurate. First, be sure to select the QuickEdit Mode and Insert Mode check boxes in your command-prompt window's Properties dialog box so that you can copy text into and out of the command-shell window. The first item on the command line is your script path. If you have a long path to your script location, you might be in for a headache: One small typo can introduce errors.
The easiest method I've found to quickly obtain the script path is Smaller Animals Software's freeware SAMenu utility. This little Windows Explorer extension gives you a right-click menu option that copies the file path to the clipboard when you right-click the file. Simply right-click your script and select Copy Path to Clipboard. Right-clicking in your command-prompt window copies the path to your script. You can similarly enter other paths to files or folders. Also, you can drag files into the command-prompt window: Simply select your script and drag it to the command-prompt window while holding down either the right or left mouse button.
Although you can copy into the command-shell window, I recommend caution. If you copy a file path and set of arguments into the window—say, from a text file—and these items contain a carriage return, the script will execute instantly when you paste in the line.
Have an Argument
Getting input into your scripts is key to writing great scripts and making them easier to leverage. Arguments are an effective and time-tested way to handle input. Be sure to download this article's code and test the scripts in your environment. They should give you some new techniques to add to your scripting tool belt.