Hey, Scripting Guy! Question

Hey, Scripting Guy! I'm trying to automate a tedious license server task. To do this, I need to loop through a text file line-by-line, grab out a user name, asset number, and license ID, and then pass those values to a batch file. How can I do all that?

-- DM

SpacerHey, Scripting Guy! AnswerScript Center

Hey, DM. Before we get started today is Friday, March 21, 2008 isn’t it? We wanted to double-check that because – in the tradition if Billy Pilgrim – the Scripting Guys seem to have become unstuck in time.

Note. So how do you even get “unstuck in time?” Well, to tell you the truth, we don’t know. Scripting Guy Peter Costantini would probably know, but the last we heard Peter had traveled back to the early 1930s to try and convince Chef Boyardee not to release Beefaroni.

And no, we have no idea why he’s trying to do that.

As it turns out, the other day the Scripting Guy who writes this column opened up the Script Center home page, and noted something a bit peculiar: he was looking at a home page that actually appeared way back in August, 2007. Upon clicking a few links here and there, he realized that the entire Script Center had reverted back to August 2007. Everything that had happened since then – the Scripting Guys’ trip to Barcelona, the Christmas holidays, the 2008 Winter Scripting Games – were gone.

Now, admittedly, there are plenty of things that happened over the past few months that the Scripting Guy who writes this column would be thrilled to have disappear. On the other hand, he wasn’t too-terribly excited about doing things like re-testing the 10,500 scripts that were submitted during the 2008 Scripting Games. Consequently, he checked with the Scripting Editor to verify that she, too had become unstuck in time. (And not just fashion-wise.) When it turned out she had, the Scripting Guys sent an email to Microsoft.com asking if someone there knew what was going on. (Always a silly question to ask anyone at Microsoft if they know what’s going on, but we didn’t know what else to do.)

In the meantime, of course, we had work to do, which meant we had questions to answer. And yes, it’s possible that we had already answered these questions, back in, say, October, 2007. However, because October, 2007 hadn’t actually occurred yet we had no way of knowing that for sure. With that in mind, here’s the answer to your question, DM. If we already answered this question, well, sit tight; there’s a good chance we’ll become unstuck in time again and that will change. Either way, here’s a script that can grab values out of a text file and then use those values as command-line parameters to a batch file:

Const ForReading = 1

Set objShell = CreateObject("Wscript.Shell")

Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Pattern = "\d{1,}"

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

Do Until objFile.AtEndOfStream
    strLine = objFile.ReadLine
    arrItems = Split(strLine, " ")
    strUserName = arrItems(0)
    strAsset = arrItems(1)

    strLicenseID = arrItems(5)
   
    Set colMatches = objRegEx.Execute(strLicenseID)  
    strLicense = colMatches(0).Value

    strCommand = "%compsec% /c C:\Scripts\Test.cmd " & strUserName & " " & strAsset & " " & strLicense
    objShell.Run strCommand, 1, True
Loop

objFile.Close

Before we launch into a description of how this script works let’s take a look at the sample text file that DM sent us:

Username1 asset1 asset1 (v24) (licserver/1000 1111), start Wed 3/19 8:50
Username2 asset2 asset2 (v24) (licserver/1000 1112), start Wed 3/19 8:55
Username3 asset3 asset3 (v24) (licserver/1000 1113), start Wed 3/19 8:59

As DM noted in his email, he needs to grab three items from each line in the text file. More specifically, he needs to grab the following:

The user name (in line 1, that’s Username1).

The asset ID (asset1).

The license number. For line 1, that’s going to be 1111, the last number within the string (licserver/1000 1111).

So how are we supposed to ferret out that information? You know what? We’re as curious to find out as you are. Let’s keep reading and see what happens.

Note. If you’ve become unstuck in time and thus have already read the answer and know how the script works, well, please keep this information to yourself. It would be a shame if you spoiled the surprise for everyone else.

The script kicks off by defining a constant named ForReading and setting the value to 1; we’ll use this constant when we open our text file. After defining the constant we then create an instance of the Wscript.Shell object, the object we’ll need in order to call an application (or, in this case, a batch file) from within our script.

Our next task is to create an instance of the VBScript.RegExp object, an object that enables us to use regular expressions within our script. We’ll talk more about the regular expression object – and why we need it – in a few minutes. For now, we’ll simply note that we set the value of the object’s Pattern property using this line of code:

objRegEx.Pattern = "\d{1,}"

If you’re not familiar with regular expressions, the Pattern property specifies the target value that we’re looking for. (If you’re not familiar with regular expressions you might also want to see our TechNet Magazine article on the subject.) Using standard regular expression syntax, the \d means that we want to search for a number. Which number? It doesn’t matter; any digit between 0 and 9 will do. The {1,} means that we need to find at least one digit; if we have more than one digit in succession, well, that’s cool, too. In other words, the syntax \d{1,} will find the value 1. It will also find the value 32, the value, 732829, the value 3747347324932, etc. etc.

Note. In DM’s sample file the license ID is always four digits; couldn’t we have specified that we wanted to search for a four-digit number and only a four-digit number? Sure; in that case we would have used this syntax: \d{4} (find only four consecutive digits). However, we have no guarantee that all the license IDs are four digits; DM might very well have a license ID of 835 or a license ID of 67453. This way we’re covered regardless of how many digits appear in the license ID.

As soon as we configure the regular expression object we create an instance of the Scripting.FileSystemObject, then use this line of code to open the file C:\Scripts\Test.txt for reading:

Set objFile = objFSO.OpenTextFile("C:\Scripts\Test.txt", ForReading)

And now the fun begins.

To start, we set up a Do Until loop that runs until the file’s AtEndOfStream property is True; that simply means we’re going to keep running through this loop until we’ve read all the lines in the file. And speaking of reading lines in the file, that’s the very first thing we do in our loop; we use this line of code to read in the first line in the text file and store it in a variable named strLine:

strLine = objFile.ReadLine

What does that mean? That means that, the first time through the loop, strLine will be equal to this:

Username1 asset1 asset1 (v24) (licserver/1000 1111), start Wed 3/19 8:50

So are we done yet? Well, no, not quite; after all, we still have to tease out the user name, asset number, and license ID. To do all that, we first use VBScript’s Split function to turn strLine into an array named arrItems:

arrItems = Split(strLine, " ")

By splitting strLine on each and every blank space, we end up with an array that contains the following items:

Username1 
asset1 
asset1 
(v24) 
(licserver/1000 
1111), 
start 
Wed 
3/19 
8:50

So is this array really all that useful to us? You bet it is. We need to retrieve the user name; well, guess what item 0 in the array just happens to be? (We won’t keep you in suspense: item 0 – the first item in the array – is the user name.) You say we need the user name? That’s fine; this line of code takes that user name (item 0 in the array) and assigns it to a variable named strUserName:

strUserName = arrItems(0)

We then use the same approach to grab the asset number (item 1 in the array) and assign that value to a variable named strAsset:

strAsset = arrItems(1)

That leaves us with the license ID. As it turns out, the license ID is item 5 in the array. (Remember, the first item in an array always has the index number 0.) That’s great, except for one problem; item 5 in the array currently looks like this:

1111),

That’s not so great.

There are several different ways that we could extract the license ID from this value. We decided that the most generic (and thus most useful and flexible) way to do this was to use a regular expression. With that in mind we assign the value of array item 5 to a variable named strLicenseID, then use the Execute method to search that value for consecutive digits:

Set colMatches = objRegEx.Execute(strLicenseID)

When you call the Execute method any matches that are found (that is, any instances of the target value) are stored in a collection; in this case that’s a collection we named colMatches. Based on the structure of our text file, we know that there will be only one match; hence we can get at the license ID (1111) simply by grabbing the value of item 0 in the collection:

strLicense = colMatches(0).Value

At this point we have all the information we need to call our batch file. That batch file requires us to pass three command-line parameters, something like this:

C:\Scripts\Test.cmd Username1 asset1 1111

That’s the command we would need to type into the command window in order to run the batch file. With one minor addition, that’s also the command we construct with this line of code:

strCommand = "%compsec% /c C:\Scripts\Test.cmd " & strUserName & " " & strAsset & " " & strLicense

As you can see, we’re simply combining the path to the batch file with the values of the variables strUserName, strAsset, and strLicense, taking care to insert a blank space between each parameter. Oh, and at the very beginning of the string we insert this construction: %comspec% /c. What’s that for? Well, that simply tells the script that we want to run this command in a new console window (%comspec%) and that we want that window to close as soon as the batch file finishes running (/c). Depending on what your batch file actually does this might be superfluous; however, this helps ensure that the batch file doesn’t run and then simply sit there until you manually close the command window.

The net result of all this? The first time through the loop the variable strCommand will end up equal to this:

%compsec% /c C:\Scripts\Test.cmd Username1 asset1 1111

Once we’ve constructed our command we can then call our batch file using the Shell object’s Run method:

objShell.Run strCommand, 1, True

Note. In case you’re wondering, the second parameter – 1 – tells the script to run the batch file in a normal window. The third parameter – True – tells the script to wait until the batch file finishes before moving on to the next line of code.

And then it’s back to the top of the loop where we repeat the process with the next line in the text file. When we’re all done we use the Close method to close Test.txt and then call it a day.

In case you’re wondering, we never did hear back from Microsoft.com. (Of course, we have no idea what month – or year – they might have sent their reply from.) However, a few minutes after we sent the email the Script Center magically reverted back to its March, 2008 edition. As far as we know, that’s a good thing. On the other hand, there’s always the chance that Microsoft.com was correct and the date really was August, 2007. If that’s the case, then the Scripting Guys have now skipped time ahead several months. If you didn’t have a Christmas this year or didn’t get any birthday presents, well, that’s our fault, and we apologize for that. But don’t despair; maybe the next time we become unstuck in time you’ll get to have two birthdays.

Note. Good point: maybe the Scripting Editor has been messing with the space-time continuum in order to ensure that she never has another birthday. We’ll look into that and let you know. (Although it doesn’t seem to be working too well, if you know what we mean.)