Hey, Scripting Guy! Question

Hey, Scripting Guy! I’m trying to write a script that will run Fsutil.exe against all the drives on a computer. That’s pretty easy. However, I’d like to have all the output written to a text file rather than displayed on screen; that’s the part that I haven’t had much luck with. Any help here would be greatly appreciated.
-- RS

SpacerHey, Scripting Guy! AnswerScript Center

Hey, RS. Well, the Scripting Guys are back from Orlando – barely. The Scripting Guys were supposed to arrive back in Seattle last night around 9:30 PM, an arrival time that – among other things – would have given the Scripting Guy who writes this column time to write this column. However, thanks to a thunderstorm that hovered directly over the airport, the Scripting Guys spent several hours sitting on the tarmac in Orlando, and didn’t actually get home until 1:00 AM Seattle time. That’s bad; even worse is the fact that, since the Scripting Guys were operating on Eastern Daylight Time, 1:00 AM Seattle time felt like 4:00 AM to them.

And, of course, there was the little matter of sitting in a cramped little seat on an airplane for 8 hours, with a good portion of that devoted to sitting on the tarmac. As you might expect, the plane was parked right next to one of the gates, but no one was allowed to leave the plane. To help make up for this inconvenience, however, the airlines did give everyone a 1-ounce bag of “Savory Snack Mix,” for free!

In the end, that means that today’s Hey, Scripting Guy! column did not get written the night before as planned. As a matter of fact, a somewhat groggy Scripting Guy is pretty much writing today’s column while you read it; we only hope we can get that last paragraph typed in before you reach the end of the article.

Note. If you could go get a cup of coffee, go to the bathroom, or otherwise stop working for a few minutes that would help us out a great deal. Hey, there’s an idea: maybe you could do some work for a few minutes.

Nah; never mind about doing some work. After all, we’d never make anyone do something that we wouldn’t do ourselves.

With all that in mind, maybe we should see what we can do about answering RS’ question:

Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
    ("Select * From Win32_LogicalDisk Where DriveType = 3")

For Each objItem in colItems
    strDriveLetter = objItem.DeviceID 
    strCommand = "fsutil dirty query " & strDriveLetter
    
    Set objExecObject = objShell.Exec(strCommand)
    Do While Not objExecObject.StdOut.AtEndOfStream
        strResults = objExecObject.StdOut.ReadAll()
    Loop

    strText = strText & strResults
Next

Set objFile = objFSO.CreateTextFile("C:\Scripts\Test.txt")
objFile.Write strText
objFile.Close

Note: If you run this script on Windows Vista, be sure to run as an administrator. (Right-click on Command Prompt and select Run As Administrator.) If you don’t, your output from this script will look something like this:

The FSUTIL utility requires that you have administrative privileges.
The FSUTIL utility requires that you have administrative privileges.

Probably not exactly what you were hoping for.

As you can see, this isn’t a particularly complicated script. (Which is good, because the Scripting Guy who writes this column isn’t particularly awake at the moment.) We kick the script off by creating instances of two COM objects: Wscript.Shell, the object we’ll use to actually run Fsutil.exe; and Scripting.FileSystemObject, the object we’ll use to write the output generated by Fsutil to a text file. After creating these two objects we connect to the WMI service on the local computer, then use this line of code to retrieve a collection of all the hard disks installed on that computer:

Set colItems = objWMIService.ExecQuery _
    ("Select * From Win32_LogicalDisk Where DriveType = 3")

Two quick notes here. First, we noted that we connected to the WMI service on the local computer. Could we run this same script against a remote computer? Well, no, not in its current state; that’s because we can’t use the Windows Script Host Exec method to kick off Fsutil on a remote machine. We could use WMI to run Fsutil, but then we'd have difficulty capturing the output and writing it to a text file. If you need to run this script against a remote machine, you’ll probably have to rely on Windows Script Host’s remote capabilities. For more information on those capabilities, see the Microsoft Windows 2000 Scripting Guide.

Second, you might note that, in our WQL query, we restricted the returned data to instances of the Win32_LogicalDisk class where the DriveType is equal to 3. As you might have guessed, anything that has a drive type equal to 3 is a hard disk (fixed disk).

So what happens after we get back this collection of hard disks? Well, to begin with, we set up a For Each loop to walk us through each item in the collection:

For Each objItem in colItems

Inside the loop, we grab the value of the DeviceID property (which just happens to be equivalent to the drive letter), and store that value in a variable named strDriveLetter. That brings us to this line of code:

strCommand = "fsutil dirty query " & strDriveLetter

What we’re doing here is constructing the Fsutil command to run against the first hard disk on the computer. Assuming that the first drive on the computer is drive C, RS needs to run the following command:

fsutil dirty query C:

By amazing coincidence, that’s the exact same command we’re constructing: we’re combining the string value fsutil dirty query and the drive letter (DeviceID) C to form that exact command.

Once we have this command we can use Windows Script Host’s Exec method to execute the command:

Set objExecObject = objShell.Exec(strCommand)

We use the Exec method here (as opposed to the Run method) for one reason and one reason only: the Exec method allows us to capture any output generated by Fsutil. In fact, that’s what this block of code is for:

Do While Not objExecObject.StdOut.AtEndOfStream
    strResults = objExecObject.StdOut.ReadAll()
Loop

All we’re doing here is waiting patiently while Fsutil does its thing. Once the command-line tool has finished running, we use the ReadAll method to read in the output (from the StdOut stream) and store it in a variable named strResults.

Note. Incidentally, we can echo back the output as well as save it to a text file. If we also wanted to see the output onscreen we could simply add the line Wscript.Echo strResults immediately after the Loop statement.

The moment we exit the Do While loop we then append the new set of data to a variable named strText:

strText = strText & strResults

From there we go back to the top of the loop and repeat the process with the next hard drive on the computer.

After we’ve run Fsutil against all the drives on the machine we use this block of code to write the complete results to a text file:

Set objFile = objFSO.CreateTextFile("C:\Scripts\Test.txt")
objFile.Write strText
objFile.Close

Needless to say, there’s nothing very fancy going on here. In line 1 we’re using the CreateTextFile method to create a new text file named C:\Scripts\Test.txt. In line 2, we use the Write method to write the value of the variable strText to this new file. Finally, in line 3 we close the text file and call it a day.

By now we’re more than just a little late with today’s column, so we’ll have to wait until tomorrow to fill you in on our trip to Orlando. We will mention, however, that the Scripting Guys got to ride the all-new Simpsons ride at Universal Studios. What was that like? Well, it was fun, but by the time we finished our stomachs were feeling a little queasy, and we definitely needed to sit down for awhile before we could even think about doing anything else.

Which, now that you mention it, does sound a lot like what people experience when they read the Hey, Scripting Guy! column, doesn’t it?