Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I concatenate all the files (taken from various folders) that have the same file name?

-- AS

SpacerHey, Scripting Guy! AnswerScript Center

Hey, AS. You know, the hardest part about selecting questions to be used in the Hey, Scripting Guy! column isn’t writing the scripts; instead, it’s figuring out how to go about testing the scripts. After all, many people write to us with questions we find both interesting and useful; unfortunately, in order to test them we’d have to set up two separate Active Directory forests, install both a Unix server and a Novell server, go back in time to the year 1542, and temporarily swap the orbits of Mars and Jupiter. Even for the Scripting Guys, that’s asking a lot.

At first glance, you might think that this question would fall into the same category; after all, how many people already have files with the same file name scattered all over their hard disk? Fortunately, though, the Scripting Guy who writes this column is lacking in both ambition and creativity; as it turns out, he had 49 different files named test.vbs located on his C: drive alone. That made it very easy for him to write—and then test—a script that grabbed all 49 of these files (regardless of location) and concatenated the contents into a file named C:\Scripts\Main_file.txt.

Interestingly enough, that was a script that looked an awful lot like this:

Const ForReading = 1
Const ForAppending = 8

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colFiles = objWMIService.ExecQuery _
    ("Select * from CIM_Datafile Where FileName = 'test' AND Extension = 'vbs'")

Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objMainFile = objFSO.OpenTextFile("C:\Scripts\Main_file.txt", ForAppending, True)

For Each objFile in colFiles
    Set objFile = objFSO.OpenTextFile(objFile.Name, ForReading)
    strContents = objFile.ReadAll
    objFile.Close
    objMainFile.WriteLine strContents
Next

objMainFile.Close

To begin with, we define a pair of constants: ForReading, which we’ll use each time we open an instance of Test.vbs; and ForAppending, which we’ll use each time we add the contents of a Test.vbs file to Main_file.txt. Following that, we bind to the WMI service on the local computer, then use this line of code to retrieve a collection of all the files on the computer that have a FileName equal to test and a file Extension equal to vbs (note that it’s just the letters vbs and not .vbs, with a dot in front of it):

Set colFiles = objWMIService.ExecQuery _
    ("Select * from CIM_Datafile Where FileName = 'test' AND Extension = 'vbs'")

This is the point where we usually say, “Of course, seeing as how we’re using WMI, you could just as easily run this script against a remote computer.” However, we aren’t going to say that this time around. Granted, we could use WMI to retrieve the equivalent collection of files from a remote computer. However, we’ll run into problems trying to open those files; that’s because the FileSystemObject isn’t designed to work with files stored on remote computers. Consequently, this particular script is for the local machine only.

Note. Of course, you could always try modifying the script so that it does work with files stored on remote computers. For a few hints on how to do that take a look at this Hey, Scripting Guy! column.

With our collection in hand we create an instance of the Scripting.FileSystemObject, then use this line of code to open the file C:\Scripts\Main_file.txt:

Set objMainFile = objFSO.OpenTextFile("C:\Scripts\Main_file.txt", ForAppending, True)

Note. What’s the second parameter – True – that we added to the OpenTextFile method? As it turns out, that’s an optional second parameter that tells the FileSystemObject to create the file Main_file.txt if that file doesn’t already exist.

Once the file is open we set up a For Each loop to walk through the previously-retrieved collection of files that have the name Test.vbs. Inside that loop we start off by opening the file in question (passing OpenTextFile the Name of the file, which is equivalent to the file path) and then reading the entire contents into a variable named strContents. That’s what we do with these two lines of code:

Set objFile = objFSO.OpenTextFile(objFile.Name, ForReading)
strContents = objFile.ReadAll

After closing the first instance of Test.vbs, we then use the WriteLine method to append the value of strContents to the file Main_file.txt:

objMainFile.WriteLine strContents

And then we loop around and repeat the process with the next instance of Test.vbs in the collection. After we’ve looped through the entire collection we close the file Main_file.txt and call it a day.

This should answer your question, AS. Oh, and by the way: thanks for asking a question that didn’t require us to go back in time. The last time we did that we went back to the year 1742 and – much to our horror – the first person we encountered was Scripting Guy Peter Costantini as a child. Hopefully that meeting won’t do anything to change the future. (Although, based on the direction the Scripting Guys are headed, maybe changing the future wouldn’t be such a bad thing.)