Scripting Text Files

By The Microsoft Scripting Guys

Sesame Script

Welcome to Sesame Script, the column for beginning script writers. The goal of this column is to teach the very basics of Windows scripting for system administration automation. We’ll provide you with the information you’ll need to begin reading and understanding scripts and to start modifying those scripts to suit your own needs. If there’s anything in particular about scripting you’re finding confusing, let us know; you’re probably not alone.

Check the Sesame Script Archives to see past articles.

On This Page

Preamble
Scripting Text Files
Creating a Text File
Closing a Text File
Writing to Text Files
Opening a Text File
Reading from Text Files
Reading and Writing from the Same File
More Information
Read and Write Your Way to a Perfect Score

Preamble

Beijing 2008. Vancouver 2010. London 2012. What do all these places and dates have in common? You guessed it: they’re the sites and years of upcoming Olympic Games. And only one, Vancouver, is the site of a Winter Olympics. But who can wait until 2010? And what’s the deal with the Winter Olympics being held only every four years? The Scripting Guys, showing a total lack of patience and an overwhelming sense of competitiveness, can’t wait that long for a major international competition. So, for the second year in a row - yes, that’s right, two years in a row, no waiting around for four years - the Script Center is hosting the International Winter Scripting Games, February 12 - 23, 2007.

But wait a minute, this is Sesame Script; why would we care about something like that in this article? After all, major competitions involve the very best from around the world; there’s no room for beginners. If you’ve sledded down the hill near your house every winter since you were 5 years old are you ready to compete in the bobsled event? Probably not. So if you’re reading Sesame Script and still trying to get the hang of all this scripting stuff, are you ready for the Scripting Games? Definitely!

Come on, it’s not like this is the Olympics or anything.

All right, it might seem a little daunting if you’re just starting out. But don’t worry, there’s an entire division this year devoted to beginners. (If you think you’ve moved beyond beginner status and are getting pretty good at scripting, there’s also an Advanced Division.) And just to make you feel even better about entering, this month’s Sesame Script will help you through at least one of the events, so you should be able to enter that one (or ones).

Therefore, in preparation for the 2007 Scripting Games (and, of course, for your general information and learning pleasure), this month we’re going to talk about text files, and, more specifically, the FileSystemObject. (If you don’t know what an object is, it might be helpful to check out the Sesame Script article Class is in Session.) And yes, this article will be extremely helpful even if you don’t enter the Scripting Games (although we can’t think of a single reason why you wouldn’t want to enter).

Scripting Text Files

Administering Windows systems without using text files would be like playing hockey without a stick. Well, okay, not exactly like that - all right, not at all like that. But nonetheless, text files are a pretty valuable part of system administration. You wouldn’t think something so simple could be so useful, but it’s true. For example, log files are typically stored as text files; so are .INI files. And it turns out that text files are a very convenient way of getting information into and out of scripts.

Fast Fact: The 2010 Winter Olympics will have 84 events (some for men, some for women, some for both) in 15 categories. The Scripting Games will have 40 events (open to anyone) in 4 categories (10 events in each category): Beginner VBScript, Beginner Windows PowerShell, Advanced VBScript, Advanced Windows PowerShell.

Creating a Text File

Suppose you need to create a log every day. For example, maybe you want a daily log of the amount of free disk space available on the C: drive of a computer. In that case, the first thing we need to know is how to create a file. We do that with the FileSystemObject, like this:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile("C:\Scripts\FreeSpace.txt")

Yes, that’s all there is to it. The first thing we do is call the CreateObject method, passing the Scripting.FileSystemObject object. This gives us a reference to the FileSystemObject and stores that reference in the variable objFSO. Next we call the FileSystemObject’s CreateTextFile method. We pass this method the name of the text file we want to create. (Note that, although we used a file with a .txt extension, any text file will work, such as - as we mentioned earlier - .log and .ini files.) In this case we gave the method the full path, but we could pass in only a file name; that would result in the file being created in the current directory. Run this script and you’ll have a file named FreeSpace.txt in your C:\Scripts folder.

Important: At this point we have an empty text file named FreeSpace.txt. If we add text to this file and save it, then call this script a second time, the script will overwrite the existing FreeSpace.txt file and create a new, empty, file. To prevent that from happening, we can add another parameter to our call to CreateTextFile:

Set objFile = objFSO.CreateTextFile("C:\Scripts\FreeSpace.txt", False)

By adding the False parameter, we tell CreateTextFile that we don’t want to overwrite an existing file. If the file exists and you run this script you’ll receive an error message:

Microsoft VBScript runtime error: File already exists

Bonus We mentioned that this might be something you want to do every day: keep a log for each day. In that case, you’d probably want to give the file a new name each day. It makes sense, then, to base the file name off of the current date. So let’s take a look at a script that will do just that:

dt = Date
dt = Replace(dt,"/","-")
strFileName = "C:\Scripts\" & dt & "-FreeSpace.txt"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile(strFileName)

In this script we call the VBScript Date method, which returns the current system date, and assign it to the variable dt. We then use the Replace method to replace all the slashes (/) in the date with dashes (-). Why do we do this? Because depending on your regional settings, your date could come back looking something like this:

2/5/2007

We’re going to be using this date in the name of our text file, and, as you know, forward slashes aren’t allowed in file names. So we call the Replace function, passing it our date (the variable dt), the character we want to replace (/) and the character we want to replace it with (-). We then assign this value back to the dt variable, which now looks like this:

2-5-2007

Next we want to use this date to create the full path to the new file, which we’re going to store in the variable strFileName. We start with the string "C:\Scripts\"; we append our date (dt) to that string, then append "-FreeSpace.txt" to the end. At this point (depending on the date, of course) our file name will look like this:

C:\Scripts\2-5-2007-FreeSpace.txt

Now we simply pass strFileName to the CreateTextFile method, and, when we run the script, we’ll have a new file named 2-5-2007-FreeSpace.txt in our C:\Scripts folder.

Closing a Text File

All we’ve done so far is create an empty text file. If we don’t want to do anything else at this point we can simply close the file and we’re done. Here’s how we close the file:

objFile.Close

It doesn’t get much easier than that.

Writing to Text Files

Okay, we’ve created a text file, now it’s time to actually put some text into this file. Let’s start by reading in some information from the computer that we want to add to our text file. Suppose we want to keep track of the amount of free space on the C: drive of the computer. We do that like this:

strComputer = "."

Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery _
    ("Select * from Win32_LogicalDisk Where DeviceID = 'C:'")

For Each objDisk in colDisks
    Wscript.Echo objDisk.FreeSpace
Next

We’re using Windows Management Instrumentation (WMI) to query the computer for information, and then, in this example, we’re echoing that information to the screen. We’re not going to go into a lot of detail about this script because that’s not what this article is about; instead, we’re just trying to give an example that uses real information. All you need to know is that this script connects to the WMI service and queries for a reference to a Win32_LogicalDisk object that has a DeviceID of C:, that means that the object refers to the C: drive. We then loop through the collection of objects returned (there will be only one) and echo the value of the FreeSpace property, which just happens to contain the amount of free space available on the C: drive.

Fast Fact: In Sarajevo in 1984 Torvill and Dean (no, not Dean the Scripting Guy but a different Dean) received perfect scores in ice dancing. In 2006, 54 people received perfect scores in the Scripting Games. (Don’t worry, Scripting Games scoring doesn’t have the same…room for interpretation…that figure skating scoring does.)

Now, back to our text file. Rather than echoing our output to the screen we want to save this value to the file. We do that by using the WriteLine method:

objFile.WriteLine (objDisk.FreeSpace)

Here we’re simply passing the value to WriteLine that we want to write to the file, in this case the value of the FreeSpace property. Let’s take a quick look at the whole script:

strComputer = "."

dt = Replace(Date,"/","-")
strFileName = "C:\Scripts\" & dt & "-FreeSpace.txt"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile(strFileName)

Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery _
    ("Select * from Win32_LogicalDisk Where DeviceID = 'C:'")

For Each objDisk in colDisks
    objFile.WriteLine (objDisk.FreeSpace)
Next

objFile.Close

We start by setting strComputer to the local computer, then retrieve the current date and create a file name and path from it:

strComputer = "."

dt = Replace(Date,"/","-")
strFileName = "C:\Scripts\" & dt & "-FreeSpace.txt"

The next thing we do is use the FileSystemObject to create our text file:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile(strFileName)

After that we perform our WMI query, then we loop through the results of our query and use the WriteLine method to write the results to our text file. We end by closing the text file:

For Each objDisk in colDisks
    objFile.WriteLine (objDisk.FreeSpace)
Next

objFile.Close

And here’s what our text file looks like now (depending on the number of bytes of free space on the computer):

Free Bytes on a Computer

We can also add more information to our output to make the text file more readable:

objFile.WriteLine ("Free space (in bytes): " & objDisk.FreeSpace)

That results in a text file that looks like this:

Free Bytes with Info

Suppose we wanted to add more information, say the total size of the disk. All we need to do is add another call to WriteLine and pass it the Size property of our Win32_LogicalDisk class:

For Each objDisk in colDisks
    objFile.WriteLine ("Free space (in bytes): " & objDisk.FreeSpace)
    objFile.WriteLine ("Total space (in bytes): " & objDisk.Size)
Next

And here’s the output:

Free and Total Bytes

Opening a Text File

Now we know how to create and write to a text file. But what do we do if we want to work with a text file that already exists? We used a method named CreateTextFile to create the text file, so logically you might assume there would be a method named OpenTextFile to open a text file. Believe or not, you’re right: there is a method named OpenTextFile. Unlike CreateTextFile, however, we can’t simply pass the filename to OpenTextFile and expect the file to open. That’s because we need to pass one additional parameter: a value that declares our purpose for opening the file.

When we created the file, seeing as how it was a newly-created file, it was empty. If you think about it, what can we do with an empty file? We wouldn’t want to read from it; there’s nothing there to read. The only thing we can possibly do is write to it. That’s why we were able to call CreateTextFile then simply start writing to it. However, when we open a text file the OpenTextFile method doesn’t know whether we plan on reading from or writing to the file. Is that important? Yes, it’s very important. The … interesting … thing about the FileSystemObject is that it needs to know what you’re going to do with the file so it will know what actions are allowed on the file once it’s opened. We have three choices:

  • ForReading - We open a file as read-only. This allows us to read the file - all at once, a line at a time, a character at a time, etc. - but we’re not allowed to write to it.

  • ForWriting - We open a file as write-only. No, not read/write, but write-only. This means we can’t actually read the contents of the file, we can only write data to the file.

  • ForAppending - This is similar to ForWriting, except that ForAppending allows us to add text to the end of an existing file, whereas ForWriting wipes out any existing text and writes new text to the file.

We have to decide which of these three things we want to do, and pass a value to OpenTextFile representing the appropriate choice. Here are the values that correspond to each choice:

ForReading

1

ForWriting

2

ForAppending

8

In this script we’re going to open our text file for reading. Here’s the call to OpenTextFile we’re going to use:

Const ForReading = 1
Set objFile = objFSO.OpenTextFile(strFileName, ForReading)

We start by declaring a constant that we’ve named ForReading and assigning it the value 1. We then call OpenTextFile and pass it the name of our text file and the ForReading value. We now have an object reference, stored in objFile, that allows us to read from our existing text file.

Tip: We can open any text file; we aren’t limited to a text file we’ve just created. By default the text file must already exist or we’ll get a “File Not Found” error message. However, we actually can use the OpenTextFile method to create a new file and open it at the same time. We do this by passing a third parameter to OpenTextFile:

Set objFile = objFSO.OpenTextFile(strFileName, ForWriting, True)

Passing a value of True as the third parameter, as we did here, creates the file if it doesn’t already exist and opens it for - in this case - writing. This statement is equivalent to using CreateTextFile.

Reading from Text Files

This is all pretty impressive so far: we now have a brand new text file that contains the free space and total disk space of the C: drive on the local computer. Well, that’s great, but what if we have 100 computers out there and we need this same information from all of them? We already mentioned that we could set the strComputer variable to the name of a remote machine, but do we really want to create 100 different scripts, or change this script 100 times, or even put in an array of 100 elements, so we can get at those computers? All of those options sound pretty tedious to us. Fortunately, there’s a much easier way. And, coincidentally enough, it involves text files.

One of the easiest ways to cycle through a list of computers is to put that list into a text file. An added bonus to this approach is that it’s simple to go in and add and remove computer names as necessary. Let’s say our list looks like this:

atl-fs-01
atl-fs-02
atl-fs-03
atl-fs-04
atl-fs-05

We save this list in a text file that we name Servers.txt. Let’s start with a simple script that will read this list of computers from the text file and display the results to the screen:

Const ForReading = 1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Servers.txt", ForReading)

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    Wscript.Echo strLine
Loop

objFile.Close

We begin this script by setting the constant ForReading to a value of 1. Remember, a value of 1 means we’ll be reading the file, we won’t be able to write to it. We then create an instance of the FileSystemObject and open the file for reading:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Servers.txt", ForReading)

We want to read the file line by line until we reach the end of the file. How do we know when we’ve hit the end of the file? Well, FileSystemObject has a very handy little property named AtEndOfStream. This property is False until you’ve read the last line of the file; at that point AtEndOfStream is set to True. We want to continue reading lines from the file until AtEndOfStream is equal to True. The easiest way to do this is by using a Do Until loop:

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    Wscript.Echo strLine
Loop

The Do Until loops says “do everything between the Do statement and the Loop statement until the condition following the keyword Until is True.” The “everything” in this case is to read a line from the file using the ReadLine method and display the line. The ReadLine method doesn’t take any parameters; it just reads the next line from the text file. ReadLine returns the line that it read, which we’ve stored in the strLine variable. We then simply echo that line. When we’re done we close the file.

We’re pretty sure that, if you look at the script that adds the disk space information to a file, you’ll now be able to add the script code you need to get the information for all of the computers listed in the file Servers.txt. Go ahead and try it. While we’re waiting, we’ll throw up another Fast Fact.

Fast Fact: 84 countries were represented in the 2006 Winter Olympics. 62 countries (real and imaginary) were represented in the 2006 Scripting Games.

Did you get it to work? There are a couple of things that might have tripped you up a little, so we’ll show you what we came up with:

Const ForReading = 1

dt = Replace(Date,"/","-")
strFileName = "C:\Scripts\" & dt & "-FreeSpace.txt"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile(strFileName)

Set objFile2 = objFSO.OpenTextFile("C:\Scripts\Servers.txt", ForReading)

Do Until objFile2.AtEndOfStream
    strComputer = objFile2.ReadLine

    Set objWMIService = GetObject("winmgmts:" _
        & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    Set colDisks = objWMIService.ExecQuery _
        ("Select * from Win32_LogicalDisk Where DeviceID = 'C:'")

    For Each objDisk in colDisks
        objFile.WriteLine ("Computer: " & strComputer)
        objFile.WriteLine ("Free space (in bytes): " & objDisk.FreeSpace)
        objFile.WriteLine ("Total space (in bytes): " & objDisk.Size)
        objFile.WriteLine ()
    Next

Loop

objFile.Close
objFile2.Close

The trickiest part of this script is that we now have to keep track of two files, the file we’re writing our output to and the file we’re reading our server names from.

We start by declaring the constant required for reading the existing file of server names:

Const ForReading = 1

Next we put together our file name and create the FileSystemObject and our new text file:

dt = Replace(Date,"/","-")
strFileName = "C:\Scripts\" & dt & "-FreeSpace.txt"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile(strFileName)

Keep in mind that we now have a new file open for writing (which is all you can do with a newly-created file, other than close it) that we’re referencing with the objFile variable. Now we want to open a different file, so we need to store that object reference in a different variable, in this case objFile2:

Set objFile2 = objFSO.OpenTextFile("C:\Scripts\Servers.txt", ForReading)

We now have two files open, one for writing (objFile) and one for reading (objFile2). We want to read through our list of servers (objFile2) until we’ve read through them all, so we need to put in our Do Until loop:

Do Until objFile2.AtEndOfStream

Inside this loop we want to retrieve the disk information for the computer we just read from the text file. In order to do that, we need to read the name of that computer from the file and assign the value to our strComputer variable, like this:

strComputer = objFile2.ReadLine

The first time through the loop, strComputer will contain the value atl-fs-01, the first computer on our list.

Next we connect to the WMI service and retrieve our logical disk information. We then write that information to our output file, objFile:

For Each objDisk in colDisks
        objFile.WriteLine ("Computer: " & strComputer)
        objFile.WriteLine ("Free space (in bytes): " & objDisk.FreeSpace)
        objFile.WriteLine ("Total space (in bytes): " & objDisk.Size)
        objFile.WriteLine ()
    Next

We’ve added two lines from our earlier write script. First, we write the name of the computer to the text file; that way we can keep track of which information goes with which computer. We also added a final call to WriteLine with no parameters. This simply adds a blank line to the file to separate the results and make it more readable.

We continue to loop around until we run out of computers. Finally, we close both files, and we’re done!

objFile.Close
objFile2.Close

Our text file will look something like this:

Info from Multiple Computers

Reading and Writing from the Same File

In our previous example we created a new file that we wrote to and also opened an existing file for reading. What if we want to find some text in an existing file and change it? In other words, what if we want to read from the file and then write to that same file? That sounds like a problem; after all, he FileSystemObject allows only one thing at a time to be going on, we can’t open a file for reading and writing. But we can work around that problem: that just means that we’ll have to open the file for reading first, then for writing. Let’s go through a simple example.

We’ll start with a simple text file. This file has a list of employees and the companies they work for stored in a text file named Companies.txt:

Ken Myer
Fabrikam

Pilar Ackerman
Wingtip Toys

Jeff Hay
Fabrikam

Ellen Adams
Northwind Traders

As it turns out, Fabrikam hasn’t been doing well for several years now and was just bought out by Contoso. That means we have to change every occurrence of Fabrikam to Contoso.

Keep in mind there are different ways of doing this, and that text files aren’t really made for searching. However, we’re stuck with this text file and there’s nothing we can do about the buyout, which means we need to make the changes. Here’s the script we came up with:

Const ForReading = 1
Const ForWriting = 2

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Companies.txt", ForReading)

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    If strLine = "Fabrikam" Then
        strLine = "Contoso"
    End If
    strContents = strContents & strLine & vbCrLf
Loop

objFile.Close

Set objFile = objFSO.OpenTextFile("C:\scripts\Companies.txt", ForWriting)
objFile.Write(strContents)
objFile.Close

We’re going to need to open an existing file for reading and for writing, so we start by defining the appropriate constants:

Const ForReading = 1
Const ForWriting = 2

Next we create a FileSystemObject and use that object to open the text file C:\Scripts\Companies.txt for reading:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\Companies.txt", ForReading)

Nothing new so far. Now we want to read through the file, looking for instances of “Fabrikam.” So we once again set up a Do Until loop, looping through the file until we’ve reached the end (AtEndOfStream is True). We then read the first line of the file:

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine

Still nothing new. Now we simply need to put in an If statement. If we’ve read in a line that contains the value Fabrikam, we want to change it to Contoso. If it doesn’t contain Fabrikam, we simply ignore that line. Here’s the If statement:

If strLine = "Fabrikam" Then
        strLine = "Contoso"
    End If

Now we have a bit of a dilemma. We’ve read from the file, and let’s say we found a line that contains Fabrikam. Our file is open for reading, meaning it’s read-only, no writing allowed. How in the world do we change that line to Contoso in the file?

The nice thing about computers these days is that they have more memory than all the Scripting Guys combined. All we have to do is store the entire contents of the text file in memory, making any changes as we go along, then write everything back into the file. We’ll store the entire contents of the file in the variable strContents. That’s what this next line is doing:

strContents = strContents & strLine & vbCrLf

What we’re doing here is taking the value of strContents (which, the first time through the loop, is nothing) and adding the line we just read (strLine). We then add a carriage-return linefeed with the built-in VBScript keyword vbCrLf to separate the lines. And remember, if the line we read was Fabrikam, we changed the value of strLine, so we’ll be adding Contoso to strContents rather than Fabrikam.

After we’ve looped through the entire text file we’ll have the entire contents of the file stored in the variable strContents. The only difference will be that strContents contains instances of Contoso, whereas the text file still contains Fabrikam.

Now it’s time to close the file. Once it’s closed, we can open it again, this time for writing:

Set objFile = objFSO.OpenTextFile("C:\scripts\Companies.txt", ForWriting)

Notice we don’t need a different variable this time. We’re not using objFile anymore, we closed that instance of the file, so we can reuse the same variable to open the file again and get a new instance.

The only thing left to do is to write the contents we’re storing in memory to the text file:

objFile.Write(strContents)

In our last example we used the WriteLine method to write to the file. That’s because we were writing one line at a time, meaning that we wanted each individual line to be written to the file and then have a carriage-return linefeed at the end. This time we made one giant file and put all the carriage-return linefeeds in ourselves, so we simply call the Write method to plop the whole thing into the text file.

Note: We just showed that you can write either one line at a time (using WriteLine) or an entire file (using Write). You can do the same with reading. We saw how to read one line at a time with the ReadLine method; we can also read the entire contents of a text file into memory all at once using the ReadAll method. See More Information below for resources that will show you how to do this - and more.

Finally, we call the Close method, which saves and closes the file. Now if we open Companies.txt, this is what we’ll see:

Ken Myer
Contoso

Pilar Ackerman
Wingtip Toys

Jeff Hay
Contoso

Ellen Adams
Northwind Traders

More Information

There is much more you can do with text files, but we’re out of time - we need to finish getting ready for the Scripting Games. If you want to learn more, here are a couple of great resources:

Reading and Writing Text Files - Windows 2000 Scripting Guide

Text Files Q & A - Hey, Scripting Guy! Archive

Read and Write Your Way to a Perfect Score

If you understood anything at all in this article, you’ll do great in the Scripting Games. We can’t guarantee a perfect score for everyone (some of the events are still going to be pretty tough), but you’ll most likely make a pretty good showing. Here’s a final Fast Fact for you:

Fast Fact: In the Olympic Games, 1st, 2nd, and 3rd place winners receive gold, silver, and bronze medals, respectively (and often lucrative endorsement deals). In the 2007 Scripting Games, 250 randomly-drawn competitors will receive a Dr. Scripto bobblehead doll. Competitors who score more than 60 points within a division will receive a Certificate of Achievement. How could an endorsement deal possibly be better than that?