Hey, Scripting Guy! How can I write a script that increments a counter each time a particular log file gets updated?
— HJ
Hey, HJ. You know, lately the Scripting Guy who writes this column has been victimized by a disturbing phenomenon: he keeps getting phone calls from machines. For the past several nights the phone has rung in the Scripting House, and each time there’s been a computer on the other end: “Hello, this is an automated survey being conducted by ….” To tell you the truth, the Scripting Guy who writes this column doesn’t like talking on the phone to begin with; as you might expect, then, he especially doesn’t like talking on the phone to a machine. With that in mind, it probably goes without saying that he’s developed his own “automated response” to these surveys.
And yet, as irritating as this is, these incidents have got him thinking about automated responses in general. Maybe, he thought, it would be possible to create an automated response mechanism for Hey, Scripting Guy! That shouldn’t be too difficult, and it would save him a ton of work. To begin with, the mechanism would need the ability to pick out a few keywords and then use those keywords to determine the question that was being asked. The machine would then have to prepare a response using the traditional Hey, Scripting Guy! format: a series of nonsensical anecdotes, ramblings, and rhetorical questions followed by a script that actually solves the problem. After playing around with this for a little while he managed to come up with a mechanism that produced the following:
Hey, HJ. You know, the Scripting Son pitched quite well last night; in fact, he had [insert number] strikeouts and only walked [insert number]. Did you see that new commercial? That has to be the [best/worst] commercial we’ve seen in a long time. That Scripting Editor, she sure is [mean], isn’t she? As for Peter Costantini, the oldest living Scripting Guy ….
Well, you get the idea. We have to admit that it’s an intriguing concept, and it is hard to tell the real column from the fake column. But it’s a moot point: after all, even the Scripting Guy who writes this column would never be so lazy as to let a machine do all his work for him.
With that in mind, why don’t … we … take a look at a script that increments a counter each time a particular log file gets updated:
intModifications = 0strComputer = “.”
Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _ (“SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE ” _ & “TargetInstance ISA ‘CIM_DataFile’ and ” _ & “TargetInstance.Name=’C:\\Scripts\\Test.txt'”)
Do Set objLatestEvent = colMonitoredEvents.NextEvent If objLatestEvent.TargetInstance.LastModified <> objLatestEvent.PreviousInstance.LastModified Then intModifications = intModifications + 1 Wscript.Echo “File modified: ” & Now Wscript.Echo “Number of modifications: ” & intModifications Wscript.Echo End If Loop
Now comes the fun part: figuring out how this script actually works. As you can see, we start out in very simple fashion: we assign the value 0 to a variable named intModifications (which, needless to say, is the counter variable we’ll use to keep a running count of each time the file in question gets modified). We then connect to the WMI service on the local computer, although – and stop us if you’ve heard this one before – you can just as easily run the script against a remote computer; all you have to do is assign the name of that computer to the variable strComputer.
That brings us to this crazy-looking chunk of code:
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _ (“SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE ” _ & “TargetInstance ISA ‘CIM_DataFile’ and ” _ & “TargetInstance.Name=’C:\\Scripts\\Test.txt'”)
Believe it or not, this is our event subscription query. We won’t explain the ins and outs of event subscriptions in today’s column; if you need more information on the subject you might want to take a look at our Scripting Week 2 webcast on WMI events. In a nutshell, we’re simply asking the script to check every second (WITHIN 1) to see if any new members of the __InstanceModificationEvent class have been created. (As the name implies, instances of this class are created any time an object is modified.) Of course, objects get modified all the time on a computer: processes use more (or less) CPU power; memory goes up and down; network connections are enabled and disabled. That’s all well and good, except that we don’t want to be notified when any of those things occur. Therefore, we’ve added a couple stipulations to our query:
• |
We only want to be notified if the new instance (the TargetInstance) of the __InstanceModificationEvent class happens to be a file (CIM_DataFile). |
• |
We only want to be notified if this new file has a Name (path) of C:\\Scripts\\Test.txt. |
Note. What’s that? Why the extra \’s in the file path? That’s easy: as it turns out, the \ is a reserved character in WMI. Therefore, any time you use a \ in a Where clause you must “escape” the character; that simply means you have to preface each \ with a second \. As a result, C:\Scripts\Test.txt gets written as C:\\Scripts\\Test.txt. |
Still with us? Good. Next we create a Do loop designed to run forever and ever; notice that we don’t include any sort of exit condition (like, say, do until something happens, or do while something is true):
Do
Inside that loop, we then use this line of code to instruct the script to sit tight and wait until the target file (C:\Scripts\Test.txt) has been modified:
Set objLatestEvent = colMonitoredEvents.NextEvent
What happens if the file never gets modified? Nothing; the script will dutifully “block” on that line of code until the universe collapses in on itself. And even then it will continue to wait for the file to be modified.
But what happens if the file does get modified? That’s going to do two things. First, it’s going to create a new member of the __InstanceModificationEvent class. Second, because this new class member meets our event subscription criteria, that’s going to cause the script to unblock. And that, in turn, causes the script to execute this line of code:
If objLatestEvent.TargetInstance.LastModified <> objLatestEvent.PreviousInstance.LastModified Then
Depending on how you want to look at things, this line of code might be optional. In testing the script we discovered that just opening the file, even without making a single change to it, triggered a new instance of the __InstanceModificationEvent class. We didn’t like that; that made it difficult to separate times when someone actually modified the file from times when someone merely looked at the file. To get around that problem, we use the preceding line of code to determine when the file was last modified (something we do by checking the value of the LastModified property). In particular, what we do is compare the LastModified property for the latest version of the file (the TargetInstance) with the LastModified property of the previous version of the file (PreviousInstance); that is, we compare the file as it exists right now with the file as it existed before the new member of the __InstanceModificationEvent class was created. If the two dates and times match then we know that the file wasn’t actually modified.
And if they don’t match that means that a change of some kind was made. Therefore, we run this little block of code:
intModifications = intModifications + 1 Wscript.Echo “File modified: ” & Now Wscript.Echo “Number of modifications: ” & intModifications Wscript.Echo
Here we start off by incrementing the counter variable intModifications by 1; that’s because a new modification just took place. We then echo back two things: 1) the fact that the file has been modified, along with the current date and time (Now); and 2) the total number of modifications that have been made (intModifications). And then we loop around and repeat the process all over again.
All in all, so easy a, uh, machine could have done it
A couple of things to keep in mind here. First, we recommend that you run this script in a command window under the CScript script host. Why? Because that way the script will be able to write its notifications to the command window and keep running. If you run it under WScript, you’ll need to dismiss several dialog boxes each time the file gets modified. Yuck.
Second, tallying takes place only while the script is running; as you probably guessed if the script is terminated then you won’t be able to keep track of modifications. (Plus the current tally will be erased forever.) Because of that, you might want to modify the script so that, instead of echoing information to the screen, it writes its tally to a text file. In addition, you might want to think about turning this into a permanent event consumer, something that would enable WMI to track file modifications without even needing a script. That’s not necessarily hard, but it can be a little tricky. For more information on permanent event consumers, see the WMI SDK on MSDN.
And now for the big question: who did write today’s column? Was it the Scripting Guy who typically writes this column, or was it some cold, unthinking machine that parsed the question as best it could, began its reply with a series of totally unrelated and irrelevant anecdotes, and then simply parroted back a script it probably copied off the Scripting Editor’s computer?
You know, when you put it like that it is hard to tell, isn’t it?
0 comments