Give all your scripts a happy ending
For most scriptwriters, getting their scripts to run correctly and produce proper output or results is the primary goal. Getting your scripts to stop properly—rather than getting hung up, exiting prematurely, or entering a continuous loop—is an entirely different can of worms. If your scripts are running as scheduled tasks, script termination problems aren't as evident as when scripts run interactively and can be difficult to troubleshoot, as the following FAQs demonstrate.
I have a script that seems to run fine the first time but then continues into a partial second run, thus adding a duplicate entry to the bottom of the output file. What am I doing wrong?
First, take a look at the sidebar "Script Termination Techniques" for a brief overview of typical script-termination options and how they work. When you use For with a Call to a subroutine or anytime you use a Call command in a script, the script flow can jump back to a location that repeats a section of code, corrupting your results. To prevent this problem, be sure to use Goto :EOF to end subroutines and use an Exit or Goto :EOF command to end the script flow.
To see an example of the problem in action, change node1 and node2 in Listing 1 to reflect the node names on your network, then try the code with the first Goto :EOF command commented out, as shown. Notice that without that command in place, when the flow finishes pinging node 2 the script flow falls through to the pingit subroutine, running it an additional time. Hence, when you look at your ping results, you'll see node1 pinged once and node2 pinged twice.
I set up a script as a scheduled task to run once early one morning. But when I reviewed the log file that the script created, it looked as though the script had run twice in a short period of time. How is that possible?
I'm guessing that you set up the script to run between 1:00 a.m. and 2:00 a.m. during the fall Daylight Savings time change. When the computer's clock automatically set back the time at 2:00 a.m., the task became scheduled to run a second time. (If you'd set the script to run between 2:00 a.m. and 3:00 a.m. during the spring time change, the task would never have run; the clock on the node would have jumped forward one hour at 2a.m..)
I run a script in Task Scheduler. The script never seems to finish running and to exit completely: Task Scheduler shows the task as running for a long time after I know it's done, then terminating at about 72 hours. However, the script produces the correct results. What's going on?
The 72-hour figure is the default setting for scheduled tasks to be terminated if they don't complete normally. (You can change that setting on the Settings tab in the Scheduled Tasks Properties dialog box.) The most common reason that tasks never complete is that the script is asking for user input such as a keystroke to end a Pause command. Many scriptwriters add one or more Pause commands while debugging a script; these commands make it easier to see script-run results by letting you run short sections of code. However, if you forget to comment out these Pause commands when you set the script to run as a scheduled task, they can cause errors.
If you want a command to wait a few seconds, then close the window automatically, you can use the Sleep command (available in the Microsoft Windows 2000 Server Resource Kit) to keep the window open for a set number of seconds at the conclusion of the script run, or you can use the Timeout command (also available in the Win2K resource kit), which keeps the window open for a set number of seconds or accepts keystroke input to close the window immediately. Listing 2 shows sample code that uses the Timeout command.
I have a script that I run as a scheduled task. When I log on to the node on which the script is running, I can see the command-prompt window output and everything looks good. But if I log out while the script is running, it seems to terminate prematurely. Is this behavior normal?
Yes, if the task is running under the same user account with which you're logged on, the script enters interactive mode and becomes visible in that user session. Because the script is in interactive mode, it will terminate prematurely when the user logs out. Therefore, the problem you describe can occur when you log on while the script is running or when the script launches during your logged-on user session.
The best way to prevent this problem is to schedule your tasks in the context of a limited-permissions service account that you create especially for running scripts. Unless you're logged on locally in the service account context, your scripts won't ever run interactively and won't be subject to accidental premature termination when you log out.
A script that I run as a scheduled task runs for some time but doesn't complete. I haven't had any luck troubleshooting the failure's root cause. What can I do?
Many of the scheduled task problems I see are caused by insufficient permissions for the account being used to run the task. Permissions that often are lacking are access to locations in which the script needs to write a remote log file or to utilities on which the script depends. Having said that, I usually run several checks to troubleshoot a scheduled script.
First, check that the scheduled task time settings allow adequate time for the script to run. Remember that default runtime is 72 hours. If the set runtime is too short, Task Scheduler will terminate the script and log an entry indicating that a premature termination was initiated.
While we're on the subject of the log file, try reviewing the scheduled tasks log file (schedlgu.txt) to see entries for the task. This file typically is in the system root (the default location is C:\windows\schedlgu.txt) or visible via the Control Panel Scheduled Tasks applet's Advanced, View Log menu option. The log file will show only recently scheduled events; if the node you're examining has many scheduled tasks, the error might have been overwritten. You'll need to look at the file immediately after the script runs.
Next, try launching the script in interactive mode from inside a command shell window. If the script runs successfully, schedule the task to run in the context of a user account, then log on with that account so that you can observe the script run (as I described previously). You might want to first add a Pause command at the end of the script so that you can scroll back up after the script has completed and observe the interactive output. Usually these techniques will help you identify the problem.
In the future, test your scripts thoroughly in interactive mode, with Echo turned on, before you make them scheduled tasks. Carefully observe the command shell output for any errors. Scheduling scripts that aren't ready for production sets you up for problems.
I want to put a Pause at the end of my script, but doing so makes the Press any key to continue message appear. I'd rather use a custom message. Any suggestions?
Follow the Pause with a redirection to NUL. Doing so will eliminate console output of the default Pause message. When you use this technique, you still need to have something in an Echoed out message that tells the user how to exit. Use the following syntax:
Echo Put your custom message here.
Echo Press any key to exit out of the script.
I understand how to use Call to move script flow to a labeled subroutine inside a batch file. I want to use Call to call a labeled subroutine in another .bat file. (The idea is to store all my common subroutines in a script that I can call from other scripts.) After that subroutine runs, I want flow to revert back to the original script. Can I use Call to do that?
Yes. Putting code sections or modules in another script is a great way to store code snippets for easy reuse. Storing reusable code sections that you've already debugged always makes good sense. However, one potential problem is that the Call command doesn't natively support calls to a label inside another .bat file. A single Goto %1 code line at the top of the called script can take care of this problem by making the flow advance to a labeled section of code that performs a function such as pinging a node or checking for memberships in a group.
First, create a collection file that contains all the code snippets you want to call from your scripts. I'll give you a sample file, collection.bat, which Listing 3 shows. This file has a Ping routine and the local group member enumeration routine. You can use this sample as a starting point and add your favorite code snippets.
The calling file, LaunchPing.bat, which Listing 4 shows, calls the :PingLabel section of code in your code collection script and launches the appropriate labeled area in the collection.bat file. The LaunchLocal.bat script, which Listing 5 shows, launches the LocalLabel section of code, which will enumerate the local groups.
I want to gather runtime statistics on each of several scripts but don't want to have to modify their code. Also, I'd like to collect the script run's elapsed time metrics output in a separate log file. How can I accomplish this?
The resource kit tool TimeThis.exe can measure the elapsed time of scripts or commands for you. I've used this tool extensively when using Robocopy as part of large data migrations; doing so helped me predict copy runtimes and cutover delays. You can use the TimeScript.bat file, which Listing 6 shows, to launch your scripts and measure their execution times without modifying the existing script code. Customize the TimeScript.bat script with the correct location of TimeThis.exe and your log file. To launch a script for which you want to capture elapsed-time metrics, use the syntax
TimeScript.bat will write the elapsed time results to the log file location you have specified.
With these script termination techniques and scheduled tasks troubleshooting tips in your scripting tool belt, you should be able to better understand how to use Exit, Exit /B, and Goto :EOF appropriately, call code in a central script, and time your script runtimes. To download the code included in this article, go to the Windows Scripting Solutions Web site (http://www.windowsitpro.com/windowsscripting), enter 47097 in the InstantDoc ID text box, then click the 47097.zip hotlink.