Hey, Scripting Guy! Question

Hey, Scripting Guy! I have a bunch of files with names like 001.jpg and 002.jpg. I also have a text file that includes the names I would like each of these files to have; for example, 001.jpg should be named Picture_of_Family_Members.jpg. How can I write a script that reads information from the text file and then renames each picture accordingly?

-- RG

SpacerHey, Scripting Guy! AnswerScript Center

Hey, RG. You know, one day Paul Simon was in a Chinese restaurant when he noticed that one of the selections on the menu was “Chicken With Egg.” Duly inspired, a short time later he wrote the classic song Mother and Child Reunion. As Casey Stengal said, you can look it up.

Now, no doubt you’re wondering what this has to do with your question. To tell you the truth, we have no idea; it’s just a piece of trivia we’ve always found interesting.

And no, it never did occur to us that other people might not find it so interesting. We’ll have to think about that a bit.

While we ponder that possibility, here’s a little something that might be a bit more relevant to your question:

Const ForReading = 1

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

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\names.txt", ForReading)

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    arrParts = Split(strLine, ",")
    strFile = "C:\\Pictures\\" & arrParts(0)
    Set colItems  = objWMIService.ExecQuery _
        ("Select * From CIM_Datafile Where Name = '" & strFile & "'")
    For Each objItem in colItems
        strNewName = "C:\Pictures\" & arrParts(1)
        objItem.Rename strNewName
    Next
Loop

objFile.Close

Before we go any further we should note that this script is perhaps a tad bit more complicated than needed; had we chosen to use the FileSystemObject to rename the files we could have cut down on both the number of lines of code as well as the cryptic nature of some of those lines. So then why didn’t we use the FileSystemObject? Well, we wanted a script that could rename the files even if those files were stored on a remote computer. Because the FileSystemObject isn’t designed for remote operations, that left us with WMI as the technology of choice. And that’s both good and bad. The good part? This script can easily be modified to work against a remote machine. The bad part? You have to be willing to put up with an eccentricity or two when it comes to locating and renaming files using WMI.

As for the code itself, we start out by defining a constant named ForReading and setting the value to 1; we’ll use this constant later on to open the text file for reading (as opposed to opening it for writing or for appending). We then use these lines of code to connect to the WMI service on the local computer:

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

Note. What’s that? Connect to the WMI service on a remote machine? We never said - oh, that’s right: we did say something about using this script against remote machines, didn’t we? Well, that’s easy enough: just set the value of the variable strComputer to the name of the remote machine. For example, this line of code will cause the script to connect to (and thus rename the files on) a remote computer named atl-fs-01:

strComputer = "atl-fs-01"

Next we need to open our text file. Here we use - well, what do you know: the FileSystemObject. Like we said, we don’t want to use this object to rename the files; however, it’s about our only recourse when it comes to reading a text file. With that in mind, these two lines of code create the FileSystemObject and then use the OpenTextFile method to open the file C:\Scripts\Names.txt for reading:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\names.txt", ForReading)

Now let’s roll up our sleeves and get to work. We’re assuming your text file looks something like this, with the current file name listed first and the preferred file name listed second:

001.jpg,Picture_of_Family_Members.jpg
002.jpg,At_the_Airport.jpg
003.jpg,Our-Hotel.jpg

With the file open we set up a Do Until loop that will simply read the file in line-by-line until there is nothing left to read. (Or, if you want to impress all your friends and neighbors, reads the file line-by-line until the AtEndOfStream property is true.) To do that, we start out by reading the first line in the file and storing the value in a variable named strLine:

strLine = objFile.ReadLine

That means that strLine is equal to this:

001.jpg,Picture_of_Family_Members.jpg

And that means we need to separate the current file name from the preferred file name. Can we do that? Heck no. But the VBScript Split function can:

arrParts = Split(strLine, ",")

What the Split function does is examine a string and chop it into separate pieces, placing each piece in an array we named arrParts. How does the function know where to make each chop? That’s easy: the second parameter passed to Split (“,”) tells the function to make a chop every time it encounters a comma. With the first line read in from the text file that means we’ll end up with an array consisting of two items:

001.jpg

Picture_of_Family_Members.jpg

Because the first item in a VBScript array always has the index number 0, we now know two things: item 0 - or arrParts(0) - represents the current file name, and item 1 - arrParts(1) - represents the desired file name.

And once we know that we can better understand what this line of code does:

strFile = "C:\\Pictures\\" & arrParts(0)

In a moment we’re going to use WMI to connect to the file C:\Pictures\001.jpg, the first file listed in the text file and the first file to be renamed. What we’re doing here is creating the full path to the file, something we do by combining C:\\Pictures\\ and the name of the file (which, as we know, is item 0 in our array).

Good question: why do we have \\’s in the path? Well, we’re going to use this path in a WMI query and, as it turns out, the \ is a reserved character in WMI. The only way to use a reserved character in a WMI query is to “escape” it; that is, to preface it with a \. As you can probably guess, a \ prefaced with another \ gives us a \\. It’s crucial that we escape each \ in the path. (And, yes, if we were dealing with a UNC path like \\atl-fs-01\public we’d have to convert it to this: \\\\atl-fs-01\\public. What a strange and wonderful world we live in.)

Speaking of WMI queries, here’s the one we use to connect to the file 001.jpg:

Set colItems  = objWMIService.ExecQuery _
    ("Select * From CIM_Datafile Where Name = '" & strFile & "'")

All we’re doing with this line of code is asking WMI to return a collection of files that have the Name C:\Pictures\001.jpg (although, for the purposes of the query, we have to call it this C:\\Pictures\\001.jpg). Because path names must be unique (you can’t have two files named C:\Pictures\001.jpg) we can rest assured that this query will bind us to the desired file.

Another good question: what if the file C:\Pictures\001.jpg doesn’t exist? That’s fine; in that case our collection will simply be a collection with 0 items in it. In turn, the script will skip the section of code that loops through the collection and instead simply go off and read in the next line from the text file.

In case you’re wondering, the section of code that loops through the collection looks like this:

For Each objItem in colItems
    strNewName = "C:\Pictures\" & arrParts(1)
    objItem.Rename strNewName
Next

We already know that, at most, we’re going to have a single item in the collection: the file 001.jpg. That means we can go ahead and rename the file. To do that we first construct a new file path, a path consisting of C:\Scripts plus the new file name (which, as you recall, is item 1 in our array):

strNewName = "C:\Pictures\" & arrParts(1)

At that point we call the Rename method, passing the variable strNewName as the sole parameter:

objItem.Rename strNewName

And then it’s back to the beginning of the loop, where we repeat the process with the next line in the text file.

Admittedly, this might seem a bit complicated. But all we’re really doing is this:

Reading a line from a text file.

Picking out the existing file name and the new file name.

Connecting to the existing file.

Renaming the existing file.

See? It’s really not all that bad after all.

Anyway, we hope that helps, RG. Right now we have to go look up more baseball and rock and roll trivia. You know, people here at Microsoft don’t appreciate all the hard work and research that goes into writing a column like this one ….