In any piece of interpreted or compiled software, the output is all that really matters. Some programs return output on the command line, whereas others condense output into a disk file in a certain directory. Other programs let the user grab and process results when necessary. For the most part, the application model and platform determine a program's behavior. Traditional GUI programs (e.g., Windows programs) display their output through windows and controls.
Scripts fall into another category of applications that mostly concern administrators and power users. Although scripts are powerful, they follow a simple, basic schema. Rather than relying on visual tools such as windows, dialog boxes, and controls, scripts are usually limited to the set of features that the language in use provides. For example, certain OS scripts, even in a GUI-centric OS such as Windows, are limited in their UI options. Windows Script Host (WSH) and VBScript are significant exceptions because they let you plug into many COM components to accomplish a variety of otherwise impossible tasks.
The model for script applications is based on the black-box model: The input data enters one end, then comes out properly transformed at the other end. A script's input channels are the read-only input console stream and the list of command-line arguments. A script's output channel is the output stream in which the code writes the results. Like many Windows GUI applications, scripts don't always provide text that represents output.
The ability to capture a script's output is a common need for users whose job involves repetitive administrative tasks. Let's review the common techniques you might use for such tasks, then take a look at some new techniques that WSH 5.6 offers.
Any OS can take any text that a given script sends to its standard I/O console and automatically redirect it to a particular device. From the script's perspective, the output stream is a virtual device that the underlying OS can map from time to time to a different physical device (e.g., a printer, the screen, a file). To achieve redirection, MS-DOS supplies an old acquaintance—the redirection operator (>)—which a generation of system users cut their teeth on.
To redirect to a file named output.txt every byte that a program (not necessarily a batch file or a script) dumps out to the standard I/O console, you would use the code
If you want to know details about a given folder's contents, you typically open your MS-DOS console box and type the Dir command. Alternatively, you can launch Windows Explorer and read through the lengthy list of files and subdirectories. If the list of items is particularly long and you need to check the items individually, both of these solutions are of little help. When I recently faced a similar problem, I rediscovered the effective simplicity of redirection. The following command writes the typical output of the Dir command into a text file:
Redirection isn't rocket science, but it is a useful resource if you need more than a glance to solve a file-related problem. This simple command provides a text-based list of files that you can format, print, and refer to.
The redirection operator turns a script's output into a data item that other software can use for further operations. For example, redirection can help you get rid of the introductory text that the Dir command generates. Listing 1 is a WSH script that runs the Dir command, redirects the output to a file, then reads it back, removing the Dir command's typical heading, such as
Volume in drive C has no label. Volume Serial Number is 1CC4-3870 Directory of C: 07/02/2002 03:58p <DIR>
To run the Dir command through the WSH shell, use the OS-specific command-shell executable (e.g., cmd.exe), whose full path resides in the %comspec% environment variable, as callout A in Listing 1 shows. The /c switch after the %comspec% variable opens the command-shell window, then closes it on completion of the Dir command.
The Dir command's output is redirected to the file called output.txt. To remove the unwanted introductory text, the script then opens output.txt and reads the output into a string variable, OutputString. At this point, the command's entire output is in memory as a string. After deleting the original file (i.e., output.txt), the script extracts only the characters that follow the ".." expression—in other words, the actual file listing.
The redirection technique is useful for capturing and using as a string any output that scripts—and executables in general—send through the standard output console. Redirection is always possible regardless of whether the program is GUI-centric or console-based. Any Windows or non-Windows program can always send text to the output console or read from it. When I discovered that the Win32 API includes low-level functions that specifically allow such freedom, I recompiled an old 32-bit GUI program that I now use throughout my company to generate input for many scripts. Before this discovery, I used the 32-bit GUI program to create a text file in a network share that scripts accessed and read. By simply adding a couple of new API calls in the code, I now can inject into the output console the same text destined to disk, thereby eliminating the need for scripts to access a file remotely. Each script can now create the dump locally.
I implemented another significant improvement later when I decided to redirect the error messages of both the executables and scripts to a separate file. With little effort, I created a simple but effective journal of errors. Again, this improvement required no advanced logic, but it's an effective piece of functionality. The following code redirects all error messages to a file called errors.txt:
To successfully implement this error redirection, the application must be designed to send its errors on the standard error output console, whose order number is set to 2.
What's New in WSH 5.6?
When I first glanced at WSH 5.6's new features, I didn't comprehend the full potential of the WshScriptExec object. This object, which is new to version 5.6, provides your scripts with status information about another script that you might have run through the WshShell object's new Exec method. Basically, the Exec method acts as a script-specific clone of the Run method that the same WshShell object exposes.
A unique aspect of WshScriptExec is that it provides access to the child script's StdIn, StdOut, and StdErr streams, giving you an easy way to grab a program's output and errors as well as a way to intervene on the program's input buffer. Also, to grab the output, you no longer need to work with disk files because everything is held in memory in a TextStream object. Let's review methods and properties in further detail.
The WshScriptExec object. You can't explicitly create an instance of the WshScriptExec object; you can obtain it only as the return value of the Exec method. You use the Exec method to run an application in a child command shell. Exec accepts only one argument: the command line that you use to run the script. The command line should appear exactly as it would if you typed it at the command prompt. For example, the code might look like
set shell = _ CreateObject("WScript.Shell") command = "%comspec% /c dir" set scriptExec = _ shell.Exec(command)
The WshScriptExec object has a Status property, which returns a value that denotes the status of the execution. Feasible values are WshRunning (0) and WshFinished (1), which mean that the program is still running or has completed, respectively. Typically, the program is free to complete its tasks and terminate as usual. However, the WshScriptExec object also features a Terminate method, which instructs the script engine to kill the process that started with the Exec method. The Terminate method tries two techniques to end the process. The first attempt uses the Windows message that usually quits an application. If this attempt fails (e.g., because the application doesn't have a queue of messages), the Terminate method abruptly terminates the process.
The standard streams. The other properties available on the WshScriptExec object are those that represent the three streams: StdIn, StdOut, and StdErr. A TextStream object (which is part of the Scripting Runtime Library object model) represents each stream.
The StdOut property exposes an object that wraps the write-only standard output channel (i.e., stdout). The stream's content becomes available as read-only text, and you can use the TextStream class's methods and the properties to manipulate the content. For example, the code in Listing 2 runs the Dir command and captures the output as a string. The calling script can't write text to the stdout channel because this functionality is an exclusive privilege of the running application, which is the sole owner of the channel. The StdOut property gives you only the right to read. Note that to read contents out of the stream, you don't need to create or access a disk file.
You can use the StdIn property to pass data to a process that the Exec method starts. The stream represents an alternative way for the process to collect its working data. For the calling script, StdIn is the perfect tool to automatically and silently execute interactive operations such as a logon. Your scripts can't read from StdIn, but the script that calls Exec can write some text to StdIn for the other process to read. For example, the following line of code automatically inserts a password:
For all TextStream objects, you can use the method AtEndOfStream to verify that the internal pointer appears at the end of the stream (that is, no more data exists to read or write).
No More Disk Files
Capturing a script's output (or even an application's output) is a common task for which many people have discovered and implemented consolidated approaches. Typically, script capture requires that you deal with disk files—in most cases, disk files created by the redirection symbol. In WSH 5.6, a new object—the WshScriptExec object—lets you obtain the same results in a more elegant and direct way that doesn't require the implicit or explicit manipulation of a disk file.