Hey, Scripting Guy!競技会へのお誘いと XML 関連のスクリプト

The Microsoft Scripting Guys

この記事で使用しているコードのダウンロード: HeyScriptingGuy2008_02.exe (151KB)

ご存知のとおり、 人々がスポーツ界の伝説について語るときはいつも同じ名前が出てきます。それは、ベーブ ルース、ペレ、モハメド アリ、ウォルター ペイトン、ScooterK と MrRat、ジョニーなどです。

あはは、それはおかしいですね。えっ、本当ですか。ScooterK と MrRat のことを本当に知らないのですか。ということは、当然皆さんは Winter Scripting Games (2 月 15 日から 3 月 3 日まで TechNet スクリプト センターで開催されます) についてあまりご存知ないでしょう。Winter Scripting Games については、microsoft.com/technet/scriptcenter/funzone/games を参照してください。ScooterK と MrRat は、2007 Winter Scripting Games の 1 つ以上の部門で満点を獲得した伝説的な競技者です。

本当にすばらしいのはここからです。現実的に考えて、ペレのような伝説的なサッカー選手になれる可能性はあるでしょうか。おそらくないでしょう。ボクシング世界ヘビー級王者になれる可能性はあるでしょうか。Scripting Guys の中にはヘビー級の要素を満たしているような人もいますが、ボクシング王者になることはやはり困難です。しかし、第 2 の ScooterK や MrRat になることは非常に簡単です。

注 : 理論的には、第 2 のベーブ ルースになることも可能です。ダブルヘッダーの 1 試合目と 2 試合目の間に 24 個のホットドッグを食べることができればよいだけの話です。

気楽にいきましょう。今日は、どうすれば第 2 の ScooterK や MrRat (さらには、第 2 の Bizzy や H2Data) になることができるかについてお話します。その方法は、スクリプト センターにアクセスして、Scripting Games に参加するだけです。2 月 15 日に 10 個の異なる競技 (10 個のスクリプト課題) が公開され、皆さんにそれらのすべてまたはいずれかをクリアしてもらいます。公開された問題を解決するためのスクリプトを作成し、Scripting Guys に電子メールでお送りください (詳しい説明については、Scripting Games のホーム ページを参照してください)。お送りいただいたスクリプトをテストし、問題が解決された場合はポイントを進呈します。見事に 10 個すべての競技をクリアすれば、皆さんもスポーツ界の伝説的人物になるでしょう。少なくとも、スクリプト スポーツ界では伝説的人物になります。これは、スポーツ界の伝説的人物になることとほぼ同じです。

Scripting Games (2 月 15 日から 3 月 3 日まで開催されます) は、楽しく取り組みがいのある競技会です。何より、Scripting Games にはだれでも参加できます。システム管理スクリプトにまだあまり詳しくない方は、初級部門に参加してください。VBScript、Windows PowerShellTM、および Perl (今年新設されました) での初心者向けの競技がそれぞれ用意されています。初級部門が少し簡単だと感じる方は、上級部門に挑戦してみてください。この部門でも、VBScript、Windows PowerShell、および Perl の競技が用意されています。

この競技会 (2 月 15 日から 3 月 3 日まで開催されることはお伝えしたでしょうか) は、まさにスクリプト シーズンの一大イベントです。お見逃しのないようにしてください。スクリプト センターのホーム ページにアクセスして、競技に向けたトレーニングのヒントとテクニックを参照し、正式に競技会が開始される 2 月 15 日になったらもう一度アクセスしてください。

念のためもう一度お伝えしておきます。競技会は 2 月 15 日から 3 月 3 日までです。

何でしょうか。そうですね、同感です。1 か月のうちに Scripting Games に関するお知らせだけでなく、新しい Hey, Scripting Guy! コラムも公開するというのは楽しみが多すぎますね。しかし、TechNet Magazine のすばらしい仲間たちが幸せでいられるように (言うまでもなく、これが私たちの第一の目標です)、あえて新しいコラムを公開することにしました。

ちょうど 1 年前 (もうそんなにたちましたか)、スクリプトを使用して XML ファイルを読み取る方法が記載されたコラムを公開しました。そのコラムでは、スクリプトを使用して XML ファイルの作成や変更、および XML ファイルへの書き込みを行う方法は紹介しませんでした。今月はその方法を紹介します。XML ファイルを作成するためのスクリプトをどのように記述すればよいかを知りたいんですよね。それならそう言ってくださればよかったのに。私たちにそう言ってから、1 年間回答を待ってくださればよかったんです。図 1 のスクリプトがその回答です。確かに複雑に見えますが、これからこのスクリプトのしくみについて詳しく説明していきます。

Figure 1 XML ファイルの作成

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"  

まず、Microsoft.XMLDOM オブジェクトのインスタンスを作成します。ご想像のとおり、このオブジェクトによって XML ファイルの操作が可能になります。目標は、図 2 のような単純な XML ファイルを作成することです。

図 2 目標とする単純な XML ファイル

図 2** 目標とする単純な XML ファイル **

この XML ファイルを作成するには、まずルート ノード (ITChecklist) を作成する必要があります。この操作を行うにはどうすればよいでしょうか。次のコードを使用します。

Set objRoot = _
  xmlDoc.createElement("ITChecklist")  
xmlDoc.appendChild objRoot  

とても簡単ですね。createElement メソッドを呼び出し、ルート ノードに付ける名前を createElement に渡しただけです。次に appendChild メソッドを呼び出し、唯一のメソッド パラメータとして新しい要素 (objRoot) へのオブジェクト参照を渡しています。これでルート ノードが作成されました。

しかしまだ続きがあります。次は、ComputerAudit ノードを作成します。ComputerAudit ノードは、1 台のコンピュータの情報を表す、ITChecklist ノードの子ノードです。次のように、ComputerAudit ノードを作成するためのコードは、ルート ノードの作成に使用したコードと似ています。

Set objRecord = _
  xmlDoc.createElement("ComputerAudit") 
objRoot.appendChild objRecord 

唯一異なるのは、ルート ノードの作成時には、XML ドキュメント自体に対して appendChild を呼び出したということです (xmlDoc というオブジェクト参照に注目してください)。ルート ノードに新しい子ノードを追加するには、XML ドキュメントではなく、ルート ノード (objRoot) に対して appendChild を呼び出します。とても簡単ですね。

ComputerAudit の子ノードとして ComputerName ノードと AuditDate ノードを追加する操作も同じくらい簡単です。ここでも、同様の手順を実行します。つまり、createElement を呼び出して新しいノードを作成し、appendChild を呼び出してこの新しいノードをファイルに追加します。

(ここでクイズです。この場合はどこから appendChild を呼び出すでしょうか。そうです、objRecord から呼び出します。objRecord は、先ほど作成した親 (ComputerAudit) ノードです)

ComputerName ノードと AuditDate ノードには値が含まれている必要があるため、これらのノードをドキュメントに追加する直前に各ノードの Text プロパティの値を指定することも重要です。

実際に ComputerName ノードを作成する次のコードをご覧になれば、上記で説明したことがおわかりいただけると思います。

Set objName = _
  xmlDoc.createElement("ComputerName")  
objName.Text = "atl-ws-001"
objRecord.appendChild objName  

ご想像のとおり、AuditDate を作成するコードもほとんど同じです。createElement を呼び出すときに別のノード名を指定し、Text プロパティに別の値を代入する必要があるだけです。

あまりに簡単だと思いませんか。

最初の (そして、この場合は唯一の) レコードのノードを作成したら、次のような単純で短いコード ブロックを実行します。

Set objIntro = _
 xmlDoc.createProcessingInstruction _
  ("xml","version='1.0'")  
xmlDoc.insertBefore _
  objIntro,xmlDoc.childNodes(0)  

上記のコードを実行すると、ファイルの先頭に <?xml version="1.0" ?> タグが挿入されます。これは、適切な形式の XML ドキュメントを作成するための操作です。

後は次のように、Save メソッドを呼び出し、C:\Scripts\Audits.xml という名前で新しいファイルを保存するだけです。

xmlDoc.Save "C:\Scripts\Audits.xml"  

これで、新しい XML ドキュメントが作成されます。

スクリプトを使用して新しい XML ファイルを作成する方法を知っていると、実際とても役立ちます。もちろん、まったく新しい XML ファイルの作成が必要になることはほとんどありませんが、既存のファイルへの新しいデータの追加が必要になることはあります。Scripting Guys はその方法を皆さんに紹介するでしょうか。

最初は、既存の XML ファイルに新しいデータを追加する方法は紹介しないつもりでした。しかし、心優しく気前のよい Scripting Guys は、TechNet Magazine の読者の皆さんとある取引をすることにしました。この TechNet Magazine を読んでいる皆さんが全員 2008 Winter Scripting Games に参加していただけるなら、それと引き換えに、スクリプトを使用して XML ファイルにデータを追加する方法を説明します。ということで、皆さん Scripting Games に参加していただけますか。

あなたのご参加をお待ちしています。そう、ミネソタ州ロチェスターのあなたです。

そうこなくっちゃ。Scripting Games では、だれもが必ず楽しい時間を過ごすことができます。本当ですよ。お約束します。

では、約束は約束なので、既存の XML ファイルにデータを追加できるスクリプトを紹介しましょう。図 3 をご覧ください。

Figure 3 XML ファイルへの追加

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"  

ご覧のとおり、XML ファイルを作成したスクリプトとそれほど変わりません。まず、Microsoft.XMLDOM オブジェクトのインスタンスを作成し、Async プロパティを False に設定しています。これで、非同期ではなく同期を取ってドキュメントが読み込まれるようになります。このことは、どのような影響を及ぼすのでしょうか。非同期にドキュメントを読み込むと、ドキュメントが完全に読み込まれていない場合でもスクリプトは続行されます。言うまでもなく、まだ存在しないドキュメントで作業を実行しようとすると、その直後に問題が発生する可能性があります。同期を取って XML ファイルを読み込むようにすると、ファイルが完全に読み込まれてからスクリプトが続行されます。

"完全に読み込まれてから" といっても気にしないでください。この処理は自動的に行われます。次に、Load メソッドを呼び出して C:\Scripts\Audits.xml ファイルを開きます。ファイルを開いたらすぐに、次のコード行を使用して documentElement クラスのインスタンスを作成します。これにより、ドキュメントのルートにバインドされます。もちろん、この場合は ITChecklist がルート ノードです。

Set objRoot = xmlDoc.documentElement

ここからは簡単です。ComputerAudit ノードの新しいインスタンスを作成し、appendChild メソッドを使用して、ファイルにこの新しいノードを追加します。その後、ComputerName ノードと AuditDate ノードの新しいインスタンスを作成し、新しい各ノードに適切な値 (それぞれ atl-ws-100 と現在の日付) を指定します。その後、先ほど作成した新しい ComputerAudit ノードにこれらのノードを追加し、Save メソッドを呼び出してファイルを保存します。これで、今度の Scripting Games 用のスクリプトを作成する作業に戻ることができます。

念のためもう一度お伝えしておきます。Scripting Games は 2 月 15 日から 3 月 3 日まで TechNet スクリプト センターで開催される予定です。

ここまでは順調ですね。新しい XML ファイルを作成し、そのファイルに新しいレコードを追加できました。これはとてもすばらしいことですが、まだ長い道のりがあります。たとえば、XML ファイルの既存のレコードを変更するにはどうすればよいでしょうか。図 4 に示すように、少なくとも 1 つはこの作業を行う方法があります。

Figure 4 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"  

このスクリプトで重要なのは、selectNodes メソッドを呼び出す部分です。selectNodes は、クエリによって返されるレコードを決定するメソッドです。ご覧のとおり、selectNodes メソッドの呼び出し時に 2 つの条件を指定しています。ここで必要なのは、ComputerName 属性が atl-ws-100 であるレコード、およびそのレコードの AuditDate 属性のみです。

注 : このスクリプトのしくみがわからないとおっしゃいましたか。その場合は、XML ファイルを使用した作業について初めて説明したときの記事 (technetmagazine.com/issues/2007/02/HeyScriptingGuy) を参照してください。selectNodes クエリ構文についてのさらに詳しい説明が記載されています。

いつもどおり、selectNodes からは指定した条件を満たすすべての XML レコードのコレクションが返されます。つまり、AuditDate 属性 (必要な唯一の属性) の値を更新する操作は、次のように For Each ループでコレクションのすべての項目を 1 つずつ処理し、その処理の中で AuditDate に新しい値を代入すれば完了します。

For Each objNode in colNodes
   objNode.Text = Date
Next

何でしょうか。一度に複数の属性を変更できるかですって。もちろん、できます。しかし残念ですが、今日は説明しません。これについては今後のコラムで説明します。

まだ質問がありますか。ファイル内に列挙されているすべてのコンピュータの監査日を更新するにはどうすればよいか、ですね。これは簡単です。図 5 のスクリプトを使用します。

Figure 5 監査日の変更

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"

このスクリプトには、前述の XML を変更するためのスクリプトと異なる部分が 1 つだけあります。このスクリプトでは、ComputerName が atl-ws-100 であるレコードのみを表示するように指定していません。その条件を除外すると、既定ですべてのレコードが返されます。

最後にもう 1 つスクリプトを見てみましょう。たとえば、長年使用した atl-ws-100 が最後にソリティアを実行し、空の彼方にある大きなコンピュータ墓地へと足を引きずりながら向かっていったとします。

神学に関する注 : コンピュータは動作しなくなったらどこに行くのでしょうか。おわかりのとおり、たいがいはいずれかの Scripting Guy に引き渡されます。たとえば、このコラムを執筆している Scripting Guy は、ラップトップ コンピュータを受け取ったことがあります。その理由は、マイクロソフトのためになることをたくさん行っているので、ラップトップ コンピュータを所有した方がよいということだそうです。この気前のよい申し出にも 1 つだけマイナス面がありました。それは、コンピュータが壊れていて、電源を入れることができなかったということです。しかし、かえって好都合だったかもしれません。ハード ディスクも搭載されていなかったのですから。

このシリコンチップの偉大なライフ サイクルを完結させるには、XML ファイルから atl-ws-100 を削除する必要があります。そのためにはどうすればよいでしょうか。図 6 をご覧ください。

Figure 6 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']")

For Each objNode in colNodes
  xmlDoc.documentElement.removeChild _
    (objNode)
Next
  
xmlDoc.Save "C:\Scripts\Audits.xml"  

ご覧のとおり、このスクリプトは、AuditDate プロパティを変更した前述のスクリプトに似ています。違いは 2 つだけです。1 つは、前述のスクリプトでは、selectNodes クエリでプロパティ値を指定しなかったということです。次のようにプロパティ値を指定すると、atl-ws-100 の XML レコード全体が返されます。

Set colNodes=xmlDoc.selectNodes _
  ("/ITChecklist/ComputerAudit " & _
   "[ComputerName = 'atl-ws-100']")

もう 1 つは、次のように、For Each ループ内で removeChild メソッドを呼び出して、ComputerName が atl-ws-100 であるすべてのレコードを削除しているということです。

xmlDoc.documentElement.removeChild _
  (objNode)

これで作業は完了です。さようなら、atl-ws-100。変わり果てた atl-ws-100。

まだ今月のコラムを読んでいる TechNet Magazine の編集者はいらっしゃいますか。**そうですか、毎月恒例のスクリプト パズルに全員が取り掛かかっていますか。それはよかったです。覗き見している編集者はいないようなので、ここで言わせてください。今していること (たとえば、この TechNet Magazine を読むこと) をやめて、スクリプト センターにアクセスし、Scripting Games の準備を始めましょう。どうせ TechNet Magazine はしばらく公開されているのですから。**Scripting Games は 1 年に 1 回この時期にしか開催されず、しかも期間が限定されています (具体的に言うと、2 月 15 日から 3 月 3 日までです)。どうぞお見逃しなく。

注 : そういえば、TechNet Magazine を読むのをやめてくださいと言うのが、皆さんがこのコラムを読み終えた後になってしまいましたね。**さあ、どうしてそうなってしまったかはわかりません...

Dr. Scripto のスクリプト パズル

パズルを解くスキルだけでなく、スクリプト作成スキルもテストする月に一度の課題です。

2008 年 2 月 : 謎の記号

コンピュータのキーボードには、見慣れないさまざまな種類の記号があります。さらに厄介なことに、これらの記号のほとんどは、VBScript または Windows PowerShell スクリプト言語の特定の用途に使用されます。各記号とスクリプトでの用途を組み合わせてみてください。1 問目は回答済みです。

ANSWER:

Dr. Scripto のスクリプト パズル

解答 : 謎の記号 (2008 年 2 月)

  

The Microsoft Scripting Guys は、マイクロソフトの仕事をしています、というよりもマイクロソフトにより雇われています。野球をプレイしたり監督したり観戦したり (または他のさまざまな活動を) しているのでない限り、彼らは TechNet スクリプト センターを運営しています。詳細については、www.scriptingguys.com を参照してください。

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; 許可なしに一部または全体を複製することは禁止されています.