Tales from the Script - April 2003

By The Scripting Guys

scrptguy

Script Output that Sticks Around

Have you ever written a script and run it from the command line only to see a blur of lines quickly scrolling past, carrying away the very information you created the script to retrieve? How do you get around this problem? Well, you could mess around with the configuration properties of the command window, making its buffer larger; in theory, that would allow you to scroll up and retrieve the information. (Why "in theory?" Because you still might retrieve more information than can be displayed in the command window.)

Of course, trying to make sense of data by scrolling up and down the command window is anything but easy. And what if you would like the output of the script to be saved in a more permanent format – dumped into a file for instance? Sounds hopeless, doesn't it? Well, it is hopeless, so see you next month.

No, we're just kidding; with scripting, there's always hope. As it turns out, there are several easy ways to create more readable output, and to create more permanent output as well. For example, you can use the command shell's redirection operator (the > symbol) to send the output to a text file instead of displaying it in the shell. This allows you to take any script that uses Wscript.Echo to display output, and instead have that output saved to a text file. And all without writing a single line of code!

Try the redirection technique with the output from the following WMI script, one that displays process information.

Set objWMI = _
   GetObject("winmgmts:{impersonationLevel=impersonate}\\.\root\cimv2")
Set colProcesses = objWMI.ExecQuery("SELECT * FROM Win32_Process")
For Each objProcess In colProcesses
   WScript.Echo "Process Name: "    & objProcess.Name
   WScript.Echo "Executable Path: " & objProcess.ExecutablePath
   WScript.Echo ".............................................."
   WScript.Echo vbCrLf
Next

Type the script into a text editor, like Notepad, and save it with a .vbs file extension. When you save the script, make sure that Notepad doesn't "helpfully" tack on an extra .txt extension, resulting in a script named something like MyScript.vbs.txt. Perhaps the easiest way to "be careful" about this is to surround your chosen file name with double quotes (for example, "DisplayProcesses.vbs") when you type it into Notepad's File name textbox. In other words:

sg040301

When you run the script by typing cscript DisplayProcesses.vbs at the command prompt, you will probably see a blur in the command window as information about the processes running on your computer go whizzing past (depending, of course, on how many processes are running on your computer).

Now, run the script again, this time redirecting its output to a text file named output.txt. Do this by typing cscript DisplayProcesses.vbs > output.txt at the command prompt. This time you shouldn't see any output at all in the command window. Why? Because instead of displaying output in the command window, you instructed the script to create a new text file called output.txt, a file you populated with the output from DisplayProcesses.vbs. Don't believe us? Well, prove this to yourself by typing notepad output.txt at the command prompt to open the newly created file in Notepad. As you will see, you now have a permanent report on the processes that were running on your computer at the time when you last ran the script. And to think your boss says you never do anything!

Oh, wait: that was our boss.

If you run the script again and redirect its output to output.txt again, the new results will overwrite the previous results stored in the file; this is simply the way the > symbol works. You can verify this by redirecting the output of the dir command to output.txt and then examining the results. Just type dir > output.txt and then notepad output.txt. You'll see that the output of the dir command has overwritten the process information that was previously stored in the file.

That's all well and good, but what if you want to append to the file instead of overwriting? In that case, simply use the >> redirection command instead. Try it. Type cscript DisplayProcesses.vbs >> output.txt at the prompt. Then type notepad output.txt and notice that the output from dir is still there and that the output from the DisplayProcesses.vbs script has simply been added to the end of the file.

Although shell redirection is a simple way to get script output into a file, you can exercise more control over how the information is stored in the file by using the FileSystemObject. The FileSystemObject is a component within the Scripting Runtime Library (included with WSH) that gives your scripts the ability to manipulate files and folders. You can copy, delete, and also read from and write to text files.

Note: Want more information about the FileSystemObject? Then check out the section of the Windows 2000 Scripting Guide titled, cleverly enough, FileSystemObject.

As an introduction to using the FileSystemObject, let's put together a really simple script that creates a file called fsoutput.txt and writes a single line to it. The single line is a heading comprised of a title and the date and time when the file was created. Here's the script:

Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("fsoutput.txt")
objNewFile.WriteLine "Header Information -- Date: " & Now()
objNewFile.Close

Try it out. Hopefully you know the drill by now: type the script into Notepad and save it with a .vbs extension (say, CreateNewFile.vbs). Type cscript CreateNewFile.vbs and then type notepad fsoutput.txt to see the results. You should see something like the following:

sg040307

As you probably figured out, saving data to a file is pretty much the same process as displaying data in the command window. The results are, admittedly, quite different, but the way you accomplish the tasks are very similar. When you print to the screen you use WScript.Echo; when you write to a file you use the WriteLine method. Other than the need to create an instance of the FileSystemObject and the need to specify the text file where the data will be saved, the two processes are the same.

Let's take our DisplayProcesses script and modify it to save data to a file instead of displaying that data in the command window. Here's what the script looks like after the modifications:

Set objWMI = _
   GetObject("winmgmts:{impersonationLevel=impersonate}\\.\root\cimv2")
Set colProcesses = objWMI.ExecQuery("SELECT * FROM Win32_Process")
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("writeoutput.txt")
objNewFile.WriteLine "Process Report -- Date: " & Now() & vbCrLf
For Each objProcess In colProcesses
   objNewFile.WriteLine "Process Name: "    & objProcess.Name
   objNewFile.WriteLine "Executable Path: " & objProcess.ExecutablePath
   objNewFile.WriteLine _
        ".............................................."
   objNewFile.WriteLine vbCrLf
Next
objNewFile.Close

Run the script and take a look at the output in Notepad. You should see something like this:

sg040302

Note: This particular script will always overwrite the file WriteOutput.txt. To append data to the file instead, open the text file rather than use the CreatleTextFile method to create a new file. For more information on opening text files (and appending data to those files), see the section of the Windows 2000 Scripting Guide titled Writing to Text Files.

OK, this is starting to look a little better than a blur of lines in a command prompt window (by the way, if this still looks like a blur to you, it might be time to clean your monitor). It would be nice, however, to add just a bit more formatting. For that matter, something more structured, like the kind of thing you see in Web-based reporting tools, would be nice. In fact, maybe we could mix in a bit of HTML and make our own Web-based reporting tool.

For those of you who aren't familiar with HTML, have no fear. You are about to receive the Scripting Guys' patented 30-second introduction to HTML. HTML is a markup language (hence the last two letters in the acronym). To understand the concept of a markup language, suppose you were asked to edit a 1,200-word essay that had absolutely no formatting. You might begin by indicating things like where paragraphs should start and end, and where a few words strung together actually comprise a section title. You'd probably do something to call out the title of the essay, and you might underline, bold, or italicize words that require emphasis or sentences that define terms.

If you wanted to indicate that something was a title, you might circle it and label it with the word "Title". Someone else might indicate a title in a slightly different way; for example, they might underline it and label it "Article Title." Either way, you would both be making up your own markup language.

So what's the difference between your markup language and HTML? Two things: 1) Software programs have been designed to interpret HTML, and 2) HTML is a predefined mark up language, and has been standardized throughout the world. Other than that, HTML is no different from your markup language: simply a way to indicate how unformatted text should be formatted for display in a Web browser.

In HTML you use tags (often pairs of them) to mark up a document. To indicate a top level heading, for instance, you surround it with the <H1> tag (which means, "From here on, make everything an H1 level heading") and the </H1> tag (which means, "Stop making everything an H1 level heading, and go back to the default style settings"). So, if your top-level heading is Windows Management Instrumentation, you'd indicate that with the following HTML:

<H1>Windows Management Instrumentation</H1>

Every HTML document has some core markup that indicates the header and body of the document. The header section can include various metadata about the document and should always include, at a minimum, a title. Here's what a minimal HTML document looks like:

<html>
<head>
<title>The Title</title>
</head>
<body>
<h1>Windows Management Instrumentation</h1>
</body>
</html>

Try typing the preceding HTML into Notepad and saving it with an .htm file extension, say, minimal.htm. Then have a look at it in Internet Explorer by typing minimal.htm at the command prompt. You should see something like the following:

sg040303

So, now you're an expert Web designer. If this were still the mid-nineties, your next step would probably be a business plan and a billion-dollar IPO. But since we're firmly in the 21st century (well, most of us, anyway), we'd better just get back to work and see if this new information can be put to some useful purpose.

Now, before we do that, we know what you're thinking. You're thinking, "Um, Scripting Guys, I'm a system administrator, not a Web developer. Why are you telling me this? I don't even own a copy of FrontPage!" Well, stop and think about what we just did: we created a Web page using Notepad. Is that because we used some supercharged Notepad XP not available to the general public? No; it's because Web pages are just text files; that means you can create them using any sort of text editor - or by using a script. You don't need FrontPage to create cool-looking Web reports.

Um, please don't tell anyone at Microsoft that we said you don't need FrontPage, OK?

All right. Let's try rewriting our DisplayProcesses script yet again, but this time we'll create an .htm file and incorporate the use of some HTML tags. Note that when you run this script, nothing will appear to happen. That's because we aren't displaying output on the screen, but instead writing the output to an HTML file

Here's our rewritten script:

Set objWMI = _
   GetObject("winmgmts:{impersonationLevel=impersonate}//./root/cimv2")
Set colProcesses = objWMI.ExecQuery("SELECT * FROM Win32_Process")
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("htmloutput.htm")
objNewFile.WriteLine "<html>"
objNewFile.WriteLine "<head>"
objNewFile.WriteLine "<title>Process Report</title>"
objNewFile.WriteLine "</head>"
objNewFile.WriteLine "<body>"
objNewFile.WriteLine "<h1>Process Report -- Date: " & Now() & _
    "</h1>" & vbCrLf
For Each objProcess In colProcesses
   objNewFile.WriteLine "Process Name: "    & objProcess.Name
   objNewFile.WriteLine "Executable Path: " & objProcess.ExecutablePath
   objNewFile.WriteLine _
       ".............................................."
   objNewFile.WriteLine vbCrLf
Next
objNewFile.WriteLine "</body>"
objNewFile.WriteLine "</html>"
objNewFile.Close

After running the script, open the resulting HTML page (named htmloutput.htm) by typing htmloutput.htm. You should see something similar to the following:

sg040304

First of all, take a look at the time this script was run: one o'clock in the morning! Geez, we have to find some other hobbies. At any rate, this is a start (we did create a Web page), but there's still some work left to do in order to make the information readable. Come to think of it, this type of data would look really great in a table. And guess what: HTML supports tables. (And a good thing it does. If it didn't, we wouldn't be able to finish this column.)

Creating HTML tables is pretty straightforward, although it can look a little cryptic at first glance. A table starts with the <table> tag and ends with the </table> tag. Rows of the table are tagged with <tr></tr> pairs and columns within each row are tagged with <td></td> pairs (we told you it could look a little cryptic at first glance). Here's an example of a simple HTML table:

<table>
<tr><td>1st column of 1st row</td><td>2nd column of 1st row</td></tr>
<tr><td>1st column of 2nd row</td><td>2nd column of 2nd row</td></tr>
</table>

Let's modify our script so that it puts the output into an HTML table. Here is the modified script:

Set objWMI = _
   GetObject("winmgmts:{impersonationLevel=impersonate}//./root/cimv2")
Set colProcesses = objWMI.ExecQuery("SELECT * FROM Win32_Process")
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("htmloutput.htm")
objNewFile.WriteLine "<html>"
objNewFile.WriteLine "<head>"
objNewFile.WriteLine "<title>Process Report</title>"
objNewFile.WriteLine "</head>"
objNewFile.WriteLine "<body>"
objNewFile.WriteLine "<h1>Process Report -- Date: " & Now() & _
    "</h1>" & vbCrLf
objNewFile.WriteLine "<table BORDER=1>"
For Each objProcess In colProcesses
   objNewFile.WriteLine "<tr>"
   objNewFile.WriteLine _
       "<td>Process Name:</td><td>" & objProcess.Name & "</td>"
   objNewFile.WriteLine "<td>Executable Path:</td><td>" & _
       objProcess.ExecutablePath & "</td>"
   objNewFile.WriteLine "</tr>"
Next
objNewFile.WriteLine "</table>"
objNewFile.WriteLine "</body>"
objNewFile.WriteLine "</html>"
objNewFile.Close

Notice that we've added BORDER=1 to the opening <table> tag. This does what you'd probably expect – it causes the table to display a border.

Admittedly, the table output might be a bit tough to follow. Here's what's going on:

We begin by writing the opening table tag <table BORDER=1> before we get into the For Each loop. This ensures that we will only have one table, which is what we want. The first statement inside the For Each loop writes the <tr> tag, which begins a new row. So each time we go through this loop, we will be starting a new row of the table. That makes sense, given that each time through the loop represents the information about a single process, information we want to display on a single line of the table.

So far so good, right? Next comes the columns within each row. One pair of <td></td> tags is used to get the property names (Process Name, Executable Path) into a column and the other set of <td></td> tags is used to get the corresponding values into a column. Lastly, before we exit the loop, we end the row with the </tr> tag.

The resulting htm page should look similar to the following:

sg040305

It probably won't win any graphic design awards, but it's perfectly readable and a whole lot better than blurry, jammed-together command prompt output. If you search around MSDN or elsewhere on the Web, you will find a plethora of information about HTML and how to make pages like this look really sharp. We'll leave those details as an exercise for you. After all, this column is about system administration scripting, not Web design.

Note: You're right; it would be cool to run this script, and then have the Web page automatically pop up on screen. How do you do that? Here's one way: just tack these two lines on at the end of the script:

Set objShell = Wscript.CreateObject("Wscript.Shell")
objShell.Run "htmloutput.htm"

We're going to finish off this month's Tales with a quick and easy way to get your output into Excel. Excel has an extremely rich and scriptable object model that enables you to create really fancy spreadsheets from your scripts. However, it's getting late and we're approaching our page limit. So, in the true spirit of scripting, let's take the path of least resistance. We'll modify our script to output to csv (comma-separated values) format, which we can then open up in Excel.

Here's the modified script:

Set objWMI = _
   GetObject("winmgmts:{impersonationLevel=impersonate}//./root/cimv2")
Set colProcesses = objWMI.ExecQuery("SELECT * FROM Win32_Process")
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("output.csv")
objNewFile.WriteLine "Process Name,Executable Path"
For Each objProcess In colProcesses
   objNewFile.WriteLine objProcess.Name & "," & _
       objProcess.ExecutablePath
Next
objNewFile.Close

This script is extremely simple. It outputs two comma-separated headings (Process Name and Executable Path) and then proceeds to output the Name and ExecutablePath properties separated by a comma. If you were to open the file in Notepad, it would look something like this:

Process Name,Executable Path
System Idle Process,
System,
Smss.exe,C:\WINDOWS\System32\smss.exe

To see the results of the script in Excel (assuming you have Excel installed on your machine), type output.csv at the command prompt. You should see something like the following:

sg040306

Sure, this is "cheating," but who's going to know? After all, it looks like a pretty nice little spreadsheet to us.

That's all we have for you this time; after all, even the Scripting Guys need a little sleep. We hope this column will get you started experimenting with output formats. We've only scratched the surface with regard to where you can send output from your scripts. For instance, ADO is a technology that enables you to output to various data stores, including Access and SQL Server, and, of course, XML is tres chic these days. And, yes, you can quite easily store your script output in XML format (XML is also just another text file), which opens up more possibilities than there are semicolons in the Windows source code.

Um, that means there are lots of possibilities, because there are lots of semicolons in the Windows source code.

Until next time, stop the insanity: Script!

For a list and additional information on all Tales from the Script columns, click here.