Hey, Scripting Guy! The Games Are Afoot! Oh, and Some XML, Too
The Microsoft Scripting Guys
Download the code for this article: HeyScriptingGuy2008_02.exe (151KB)
You know, any time people talk about the legends of the sporting world, the same names inevitably come up: Babe Ruth. Pele. Muhammad Ali. Walter Payton. ScooterK and MrRat. Johnny—
Ha, that's funny, you ... oh, you're serious? You actually don't know who ScooterK and MrRat are? Well, for those of you who obviously don't keep up with the Winter Scripting Games (February 15th through March 3rd at the TechNet Script Center; go to microsoft.com/technet/scriptcenter/funzone/games), ScooterK and MrRat are true legends, competitors who earned perfect scores in at least one division in the 2007 Winter Scripting Games.
And here's the really cool part: can you realistically hope to become a soccer legend like Pele? Probably not. Can you ever hope to become the heavyweight boxing champion of the world? Well, some of the Scripting Guys seem to have the heavyweight part down, but that boxing champion bit is a much tougher nut to crack. But you—yes, you—can very easily become the next ScooterK or the next MrRat.
Note: In theory, you might be able to become the next Babe Ruth, too. All you would have to do is be able to eat 24 hot dogs between games of a baseball doubleheader.
Take it easy; we'll tell you how you can become the next ScooterK or MrRat (and maybe even the next Bizzy or H2Data). All you have to do is show up at the Script Center and take part in the Scripting Games. On February 15th we're going to post 10 different events (10 scripting challenges) and dare you to complete any or all of them. Write a script that solves the problem we pose and e-mail it to the Scripting Guys. (You'll find full instructions at the Scripting Games home page.) We'll test your script and, if it works, award you points. Successfully complete all 10 events and you too will become a legend of the sporting world. Or at least of the scripting sporting world, which is pretty much the same thing.
The Scripting Games (February 15th through March 3rd) are fun and challenging. Best of all, the Scripting Games are for everyone. Are you still somewhat new to system administration scripting? Then enter the Beginners Division; we'll have separate competitions for beginners in VBScript, Windows PowerShellTM, and (new this year) Perl. Do you find the Beginners Division a bit easy? Then you should try the Advanced Division, which also has competitions in VBScript, Windows PowerShell, and Perl.
The Games (did we mention they run from February 15th through March 3rd?) are quite simply the event of the scripting season, and you don't want to miss them. Visit the Script Center home page now for tips and tricks on training for the competition, then come back on February 15th when we officially let the Games begin.
February 15th through March 3rd. Just in case you missed it.
What's that? Actually, we agree with you: coming out with a new Hey, Scripting Guy! column on top of the announcement about the Scripting Games probably is too much excitement for a single month. However, in order to keep the good folks at TechNet Magazine happy (our primary goal in life, needless to say), we decided to risk it and put out a new column anyway.
Exactly one year ago (wow, has it really been that long?), we published a column that explained how you could use a script to read an XML file. What that column didn't tell you was how you could use a script to create, write, and modify an XML file. This month, we're here to right that wrong. You say you'd like to know how to write a script that can create an XML file? All you had to do was ask. Well, ask and then wait a year for us to get around to it. Figure 1 shows the script. Admittedly, this might look complicated, but we'll explain how it all works.
Figure 1 Creating an XML file
Set xmlDoc = _ CreateObject("Microsoft.XMLDOM") Set objRoot = _ xmlDoc.createElement("ITChecklist") xmlDoc.appendChild objRoot Set objRecord = _ xmlDoc.createElement("ComputerAudit") objRoot.appendChild objRecord Set objName = _ xmlDoc.createElement("ComputerName") objName.Text = "atl-ws-001" objRecord.appendChild objName Set objDate = _ xmlDoc.createElement("AuditDate") objDate.Text = Date objRecord.appendChild objDate Set objIntro = _ xmlDoc.createProcessingInstruction _ ("xml","version='1.0'") xmlDoc.insertBefore _ objIntro,xmlDoc.childNodes(0) xmlDoc.Save "C:\Scripts\Audits.xml"
To begin with, we create an instance of the Microsoft.XMLDOM object. As you might have guessed, this is the object that enables us to work with XML files. Our goal is to produce a simple XML file that looks like Figure 2.
Figure 2 Our target: a simple XML file
To create this XML file, the first thing we need to do is create the root node (ITChecklist). How do we do that? Like this:
Set objRoot = _ xmlDoc.createElement("ITChecklist") xmlDoc.appendChild objRoot
That was pretty easy, wasn't it? All we had to do was call the createElement method, passing createElement the name we want to give to the root node. We then simply call the appendChild method, passing the object reference to our new element (objRoot) as the sole method parameter. At that point, we have ourselves a root node.
But wait; there's more. Next we create the ComputerAudit node, a child node of the ITChecklist node that represents the information for a single computer. As you can see, the code to create this node is similar to the code used to create the root node:
Set objRecord = _ xmlDoc.createElement("ComputerAudit") objRoot.appendChild objRecord
The only difference is this: when we created the root node, we called appendChild on our XML document itself. (Note the object reference xmlDoc.) To add a new child node to the root, we call appendChild on the root node (objRoot) rather than the XML document. It's that easy.
It's equally easy to add the ComputerName and AuditDate nodes as child nodes of ComputerAudit. To do that, we're going to go through a similar process: we're going to call createElement to create a new node, and we're going to call appendChild to append this new node to the file.
(Quick quiz: Where do we call appendChild from this time around? Yes, that's right: from objRecord, the parent (ComputerAudit) node we just took the time to create.)
Oh, and because the ComputerName and AuditDate nodes need to contain values, it's important that we also specify a value for the Text property of each of these nodes right before we add them to the document.
That should all become clear once we take a peek at the code that actually creates the ComputerName node:
Set objName = _ xmlDoc.createElement("ComputerName") objName.Text = "atl-ws-001" objRecord.appendChild objName
As you might expect, the code for creating AuditDate is almost identical. We just have to specify a different node name when calling createElement and assign a different value to the Text property.
Man, we're making this too easy on everyone, don't you think?
After we finish creating the nodes for our first (and in this case, only) record, we execute this sweet little block of code:
Set objIntro = _ xmlDoc.createProcessingInstruction _ ("xml","version='1.0'") xmlDoc.insertBefore _ objIntro,xmlDoc.childNodes(0)
That simply inserts the <?xml version="1.0" ?> tag at the beginning of the file, ensuring that we have a well-formed XML document.
At that point, all we have left to do is call the Save method and save our new file as C:\Scripts\Audits.xml:
Just like that, we have a brand-new XML document.
Now, that's actually pretty useful: you now know how to create a brand-new XML file using a script. Of course, the odds are pretty good that most of the time you don't really need to create a brand-new XML file; instead, you simply need to append new data to an existing file. So will the Scripting Guys show everyone how to do that?
You know, originally we decided that the answer to that question would be no, we wouldn't show everyone how to append data to an existing XML file. And then, being kind-hearted and generous people, we decided to try and strike a deal with the readers of TechNet Magazine: if everyone reading this magazine agrees to enter the 2008 Winter Scripting Games, then, in return, we'll show you all how to use a script to append data to an XML file. So, does everyone agree to enter the Scripting Games?
Um, we're waiting on you. Yes, you—the guy in Rochester, MN.
There, that's more like it. And trust us, you'll have a good time during the Scripting Games; everyone always does. We promise.
And now, seeing as how a deal's a deal, we'll show you a script that can append data to an existing XML file. Just take a look at Figure 3.
Figure 3 Appending to an XML file
Set xmlDoc = _ CreateObject("Microsoft.XMLDOM") xmlDoc.Async = "False" xmlDoc.Load("C:\Scripts\Audits.xml") Set objRoot = xmlDoc.documentElement Set objRecord = _ xmlDoc.createElement("ComputerAudit") objRoot.appendChild objRecord Set objFieldValue = _ xmlDoc.createElement("ComputerName") objFieldValue.Text = "atl-ws-100" objRecord.appendChild objFieldValue Set objFieldValue = _ xmlDoc.createElement("AuditDate") objFieldValue.Text = Date objRecord.appendChild objFieldValue xmlDoc.Save "C:\Scripts\Audits.xml"
As you can see, this isn't too terribly different from the script that created the XML file. We start out by creating an instance of the Microsoft.XMLDOM object, then setting the Async property to False, which tells the script that we want to load the document synchronously rather than asynchronously. What difference does that make? Well, if we loaded the document asynchronously, the script would be free to continue even though the document was not fully loaded. Needless to say, trouble can arise in a hurry if you try to perform a task on a document that doesn't exist yet. By ensuring that our XML file loads synchronously, we also ensure that the file will be fully loaded before our script proceeds.
Speaking of fully loaded—well, never mind; we'll just let that one go. Instead, we call the Load method to open the file C:\Scripts\Audits.xml. As soon as the file is open, we use this line of code to create an instance of the documentElement class, which has the net effect of binding us to the document root. In this case, of course, that's the ITChecklist node:
Set objRoot = xmlDoc.documentElement
It's all downhill from that point. We create a new instance of the ComputerAudit node, using the appendChild method to add that new node to the file. We then create new instances of the ComputerName and AuditDate nodes, specifying the appropriate values (atl-ws-100 and the current date, respectively) for each new node. We tuck these two items inside the new ComputerAudit node we just created, call the Save method to save the file, and then happily go back to working on our scripts for the upcoming Scripting Games.
Which, in case we didn't mention it, are scheduled for February 15th through March 3rd at the TechNet Script Center.
So far, so good. We can create a new XML file, and we can add new records to that file. That's all pretty cool, but we still have a ways to go; for example, how do we modify existing records in the file? Well, here's at least one way to do that, as shown in Figure 4.
Figure 4 Modifying the XML
Set xmlDoc = _ CreateObject("Microsoft.XMLDOM") xmlDoc.Async = "False" xmlDoc.Load("C:\Scripts\Audits.xml") Set colNodes=xmlDoc.selectNodes _ ("/ITChecklist/ComputerAudit " & _ "[ComputerName = 'atl-ws-100']/AuditDate") For Each objNode in colNodes objNode.Text = Date Next xmlDoc.Save "C:\Scripts\Audits.xml"
The important part of this script occurs when we call the selectNodes method, a method that determines which records our query will return. As you can see, when we called selectNodes we were careful to specify two criteria: we only want records where the ComputerName attribute is equal to atl-ws-100, and we only want to get back the AuditDate attribute.
Note: You say you can't see how that works? Then take a look at our first article on working with XML files (technetmagazine.com/issues/2007/02/HeyScriptingGuy); the selectNodes query syntax is explained in more detail there.
As it is wont to do, selectNodes returns a collection of all the XML records that meet the specified criteria. In turn, that means we can update the value of the AuditDate attribute (the only attribute we asked for) simply by setting up a For Each loop to walk through all the items in the collection and then, within that loop, assigning a new value to AuditDate:
For Each objNode in colNodes objNode.Text = Date Next
What's that? You're wondering if we could modify more than one attribute at a time? Yes, we could. But sadly, not today; that's something we'll have to address in a future column.
You say you have another question: How would you update the audit date for all the computers in the file? That's easy; you'd use the script in Figure 5.
Figure 5 Changing the audit date
Set xmlDoc = _ CreateObject("Microsoft.XMLDOM") xmlDoc.Async = "False" xmlDoc.Load("C:\Scripts\Audits.xml") Set colNodes=xmlDoc.selectNodes _ ("/ITChecklist/ComputerAudit/AuditDate") For Each objNode in colNodes objNode.Text = Date Next xmlDoc.Save "C:\Scripts\Audits.xml"
The only difference between this script and our previous XML-modifying script? With this one we didn't specify that we only wanted to view those records where the ComputerName was equal to atl-ws-100. By leaving out that criteria, we get back all the records by default.
Let's take a look at one last script before we go. Suppose our old pal atl-ws-100 has played its last game of solitaire and has shuffled off to that great computer graveyard in the sky.
Theological note: So where do computers go when they die? As it turns out, they're usually given to one of the Scripting Guys. For example, the Scripting Guy who writes this column was once given a laptop computer because, "You do a lot of good things for Microsoft, and you really should have a laptop computer." The only downside to this generous offering? The computer was broken and couldn't actually be turned on. Which was probably just as well. As it turned out, it didn't have a hard disk either.
In order to complete the great circle of silicon life, we need to delete atl-ws-100 from our XML file. How are we going to do that? Check out Figure 6.
Figure 6 Deleting from the XML file
Set xmlDoc = _ CreateObject("Microsoft.XMLDOM") xmlDoc.Async = "False" xmlDoc.Load("C:\Scripts\Audits.xml") Set colNodes=xmlDoc.selectNodes _ ("/ITChecklist/ComputerAudit " & _ "[ComputerName = 'atl-ws-100']") For Each objNode in colNodes xmlDoc.documentElement.removeChild _ (objNode) Next xmlDoc.Save "C:\Scripts\Audits.xml"
As you can see, this resembles our previous script, the one that modified the AuditDate property. There're really only two differences here. For one, notice that we didn't bother to specify any property values in our selectNodes query; when we do that, we get back the entire XML record for atl-ws-100:
Set colNodes=xmlDoc.selectNodes _ ("/ITChecklist/ComputerAudit " & _ "[ComputerName = 'atl-ws-100']")
And then, within our For Each loop, we simply call the removeChild method to delete all the records where the ComputerName is equal to atl-ws-100:
xmlDoc.documentElement.removeChild _ (objNode)
And that's it. Farewell, atl-ws-100; we hardly knew ye.
OK, any TechNet Magazine editors still reading this month's column? You say they've all gone off to try the monthly scripting puzzle? Excellent. Now that no one's reading over our shoulders, we can tell you this: drop whatever you're doing (like, say, reading this magazine) and get on over to the Script Center and start preparing for the Scripting Games. After all, TechNet Magazine will be here for a long time to come. But the Scripting Games are only here once a year, and for a limited time at that (specifically, from February 15th through March 3rd, just so you know). Don't miss out!
Note: You know, now that you mention it, we did wait until you'd read our entire column before we told you to stop reading TechNet Magazine, didn't we? Wonder how that happened ...
Dr. Scripto's Scripting Perplexer
The monthly challenge that tests not only your puzzle-solving skills, but also your scripting skills.
February 2008: Mystery Symbols
The computer keyboard includes all sorts of crazy-looking characters; even crazier is the fact that most of these characters have a specific use in the scripting languages VBScript or Windows PowerShell. See if you can match each symbol with its scripting use. The first one has been done for you.
Dr. Scripto's Scripting Perplexer
Answer: Mystery Symbols, February 2008
The Microsoft Scripting Guys work for—well, are employed by—Microsoft. When not playing/coaching/watching baseball (and various other activities), they run the TechNet Script Center. Check it out at www.scriptingguys.com.
© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.