Hey, Scripting Guy!それほどコツコツ働かなくても報われる

The Microsoft Scripting Guys

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

何百年 もの間、コツコツ働けば必ず報われると信じられてきました。真の充足感は、1 日真面目に働くことによって得られるもので、真の幸せは、過程ではなく、終着点で得られるものだと。ほかにもありますが、言いたいことは、わかっていただけたと思います。(シンデレラみたいに) 不平不満を口にすることなくコツコツ働けば、いつか報われる日が来るのです。

注 : コツコツ働けば、今よりも幸せになるだけでなく、いつかその苦労が報われる日が来るというのは本当なのか、ですって。どうしてそんなことを私たちに聞くのですか。

言うまでもなく、シンデレラは幸運の持ち主でした。なにしろ、意地悪な継母とその連れ子である 2 人の異母姉妹と暮らしていたら、コツコツ働かなければならない機会はごまんとあります。けれども、彼女のような幸運の持ち主でない場合はどうしたらよいでしょうか。意地悪な継母と 2 人の異母姉妹と暮らしていない場合はどうしたらよいのでしょうか。また、報われることがない長時間に渡る厳しい労働にはどうやって耐えたらよいのでしょうか。それに、真の幸せは本当に見つかるのでしょうか。

あくせく働くことを決意している方に Scripting Guys がお勧めする作業は、図 1 に示すような、きれいに整ったテーブル形式でデータを出力するスクリプトを記述することです。

図 1 テーブル形式の出力

図 1** テーブル形式の出力 **(画像を拡大するには、ここをクリックします)

繰り返しになりますが、シンデレラは強運の持ち主でした。彼女は家の中をすみずみまで掃除する必要がありました。また、その作業が終わった後に彼女が休める場所は、灰とすすにまみれた暖炉でした。けれども、このように過酷な労働に耐えたシンデレラでも、表形式でデータを表示するスクリプトを記述することには二の足を踏むでしょう。灰とすすの中で暮らすことと、表形式でデータを出力するスクリプトを記述することは、まったく別物です。

注 : ただし、暖炉の中に座ってスクリプトを記述しなければならないというのでなければ、これらの作業は同じようなものです。

シンデレラが、この作業に二の足を踏むのは、この作業が途方もなく難しいからです。VBScript を使用して表形式でデータを表示する唯一の方法の概要は下記のとおりです。

  • まず、各列の最大サイズを決めます。たとえば、サービスの表示名に 52 文字分の領域を使用する必要があるとします。
  • 次に、各データに含まれている文字数を算出します。たとえば、Adobe LM Service というサービスの表示名の文字数は 16 文字です。
  • 表示名が 52 文字の制限を超える場合、表示名用に割り当てた領域に収めるために、表示名の末尾から切り詰める必要がある文字数を特定します。表示名が 52 文字の制限を超えない場合は、表示名の長さを 52 文字にするために、文字列の末尾に追加する必要がある空白スペースの数を特定します。
  • 次のデータ セットについても同じ処理を繰り返します。また、それ以降のデータ セットについても同様です。

シンデレラの物語に詳しい方は、突然、妖精が現れて、ネズミをシステム管理者に変身させて、あなたの代わりにスクリプトを記述してくれる可能性があることはご存知ですよね (ネズミをシステム管理者に変身させることが、どれだけ簡単かということについてのコメントは差し控えさせていただきます)。けれども、そのような魔法はあてにすべきではなく、ここでは、自力で対応する必要があります。

ただし、朗報があります。このスクリプトを記述すると、世界中で最高の充足感を得ることができます。それは、このスクリプトを記述するということは、世界中で最もコツコツ働いている人になるからです。しかも、圧倒的にです。

そうですね。それほどコツコツ働かずに、ちょっとした充足感を得たいと思っている人もいらっしゃるでしょう。そのような方は「Scripting Guys は、ネズミをシステム管理者に変身させて、データを表形式で出力するようにネズミを誘導する方法を教えてくれる本当にありがたい存在だ」という風にお考えではないですか。大変申し訳ないのですが、そのような皆さんに残念なお知らせがあります。Scripting Guys は、ネズミをシステム管理者に変身させる方法については皆目見当も付きません (お役に立つかどうかわかりませんが、システム管理者をネズミに変身させることはできます)。スクリプトの代筆者を用意する方法もわかりませんし、表形式でデータを出力するスクリプトについては知る由もありません。

しかし、問題ありません。多くの皆さんが該当者になるとは思いますが、Windows® XP または Windows Server® 2003 を実行している場合は、あなたの代わりにスクリプトを記述するネズミは必要ありません (大変申し訳ありませんが、このスクリプトは Windows Vista® では動作しません)。Microsoft.CmdLib オブジェクトを使用すると、ご自身で、しかも最小限の労力でスクリプトを記述することができます。図 2 のサンプル スクリプトをご覧ください。

Figure 2 表形式でデータを出力するスクリプト

Dim arrResultsArray()
i = 0

Set objCmdLib = _
  CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = _
  WScript.Application

arrHeader = Array("Display Name", _
  "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" _
  & strComputer & "\root\cimv2")

Set colServices = objWMIService.ExecQuery _
  ("Select * FROM Win32_Service")

For Each objService In colServices
  ReDim Preserve arrResultsArray(i)
  arrResultsArray(i) = _
  Array(objService.DisplayName, _
    objService.State,objService.StartMode)
  i = i + 1
Next
objCmdLib.ShowResults arrHeader, _
  arrResultsArray, arrMaxLength, _
  strFormat, blnPrintHeader, arrBlnHide

ご参考までに、Microsoft.CmdLib は、Windows XP と Windows Server 2003 に同梱されている COM オブジェクトで、さまざまな機能が用意されています。詳細については、コマンド プロンプトで「cmdlib.wsc /?」と入力して、画面上に表示されるスクリプト ファイルのコメントを参照してください。今日のスクリプトを記述する際に必要な Microsoft.CmdLib の機能は、表形式でデータを表示する機能だけです。

ここで当然ある疑問が浮かびます。「データを表形式で表示するには、どうしたらよいのか」という疑問です。では、その方法を解明してみましょう。ご覧のように、サンプル スクリプトでは、まず arrResultsArray という名前の動的な配列を定義しています。

Dim arrResultsArray()

一般的なスクリプトでは、データを取得すると、すぐに画面上にエコー バックします。しかし、Scripting Guys は、一般的なやり方を好みません。ですから、このスクリプトでは、データを取得しても、すぐにはエコー バックしません。代わりに、返された全データを配列に格納して、Microsoft.CmdLib オブジェクトを使用して、データの書式を設定して表示することにしました。

つまり、それが、このスクリプトの冒頭で動的な配列を作成した理由です (動的な配列とは、スクリプトの実行中にサイズを変更できる配列です)。配列を定義したら、i という名前のカウンタ変数の値を 0 に設定します。この変数は、配列の現在のサイズを追跡するのに使用します。

ご参考までに、i の値を 0 に設定した理由を説明しておきましょう。配列の最初の要素のインデックス番号は必ず 0 になります。ですから、配列に 1 つ目の要素を追加する際には、要素 1 ではなく要素 0 を追加することになります。

次に、Microsoft.CmdLib オブジェクトのインスタンスを初期化する必要があります。この処理を行うのが、次の 2 行のコードです。

Set objCmdLib = _
CreateObject("Microsoft.CmdLib")
Set objCmdLib.ScriptingHost = WScript.Application

では、次の小さなコード ブロックに移りましょう。

arrHeader = Array("Display Name", "State", "Start Mode")
arrMaxLength = Array(52, 12, 12)
strFormat = "Table"
blnPrintHeader = True
arrBlnHide = Array(False, False, False)

ここでは、出力データを構成するためのいくつかのパラメータを設定しています。1 行目では、arrHeader という名前の配列に値を設定しています。ご想像のとおり、これらの値は出力する表の各列の見出しです。このサンプル スクリプトでは、コンピュータで実行中のサービスに関する情報を取得し、各サービスの DisplayName プロパティ、State プロパティ、および StartMode プロパティの値を表示します。

たいして驚くことではありませんが、配列には、Display Name、State、および Start Mode という列名を設定しています (列には単に A、B、C という名前や、Larry、Moe、Curly など、他の名前を設定することは可能です。列名をプロパティ名と同じにする必要はありませんからね)。

注 : シンデレラの物語には多くのバリエーションがあります。たとえば、異母姉妹の名前が異なったりします。ディズニー版のシンデレラの物語では、異母姉妹の名前は Drizzella と Anastasia です。これは偶然にも、編集者の名前とミドル ネームと同じです。

2 行目のコードでは、arrMaxLength という名前の別の配列に値を設定しています。この配列は、表の各列のサイズを保持するためのものです。このサンプル スクリプトの出力では、列 1 (サービスの表示名) に 52 文字分の領域を割り当て、サービスの状態と開始モードの列については、それぞれ 12 文字分の領域を割り当てます (Microsoft.CmdLib オブジェクトでは、列間に 1 つの空白文字が自動で挿入されます)。各列のサイズを 52 文字、12 文字、12 文字にするために、次のコードを使用しています。

arrMaxLength = Array(52, 12, 12)

3 行目では、データの出力形式を指定しています。ここではデータを表形式で表示する必要があります。そのため、(出力の種類を保持する) strFormat 変数に Table という値を代入しました。データを表形式ではなく、コンマ区切りの一覧として表示する必要がある場合は、次のように出力の種類を CSV と指定すれば問題ありません。

strFormat = "CSV"

このように指定すると、データは次のような状態で出力されます。

"Display Name","State","Start Mode"
"Adobe LM Service","Stopped","Manual"
"Adobe Active File Monitor V4","Stopped","Manual"
"Alerter","Stopped","Manual"
"Application Layer Gateway Service","Running","Manual"
"Apple Mobile Device","Running","Auto"
"Application Management","Stopped","Manual"

Microsoft.CmdLib オブジェクトによって、各項目間にコンマが挿入されるだけでなく、各値が二重引用符で囲まれています。これは、皆さんが想像していたよりも、すてきな機能だと思います。なにしろ、このような処理を手動で行う場合には、次のようなコードを使用する必要がありますから。

Wscript.Echo Chr(34) & objService.DisplayName & Chr(34) & "," & Chr(34) & objService.State & Chr(34) & "," & Chr(34) & objService.StartMode & Chr(34)

こんな複雑なコードは勘弁してほしいですよね。

何ですって。表形式と CSV 形式のどちらも好きではない、とおっしゃるのですか。そのような場合は、出力の種類を List と指定してみてください。そうすると、出力結果は次のようになります。

Display Name: Adobe LM Service
State:        Stopped
Start Mode:   Manual
Display Name: Adobe Active File Monitor V4
State:        Stopped
Start Mode:   Manual

それはさておき、本題に戻りましょう (私たちの話が脱線するのは、よくあることですよね)。出力形式を定義したら、blnPrintHeader という名前の変数の値を True に設定します。

blnPrintHeader = True

この blnPrintHeader 変数は、Microsoft.CmdLib オブジェクトに対して、列見出しを出力するように指示するのに使用します。列見出しを出力しない場合はどうするのか、ですって。問題ありません。その場合は、blnPrintHeader 変数の値を False に設定してください。

blnPrintHeader = False

最後に、次のコード行を使用します。

arrBlnHide = Array(False, False, False)

データを出力する段階では、列を表示または非表示にする Microsoft.CmdLib オブジェクトのオプションを使用できます。特定の列を非表示にするには (つまり、特定のプロパティのデータを出力しない場合は)、そのプロパティに対応する値を True に設定します。列を表示するには、False に設定します。

このサンプル スクリプトの出力では、DisplayName プロパティ、State プロパティ、および StartMode プロパティの値を、この順序で表示します。ですから、配列では、False, False, False という値を使用します。DisplayName プロパティの値を表示して、State プロパティの値を非表示にして、StartMode プロパティの値を表示する場合はどうするのか、ですって。その場合は、次のコード行を使用します。

arrBlnHide = Array(False, True, False)

列を表示する場合は False、非表示にする場合は True を設定することを覚えておいてください。

これで、データを出力する準備が整いました。まあ、実際にデータを保有していたら準備が整っているはずです (Scripting Guys は、これまでにデータを出力できないスクリプトを記述して、そのスクリプトのデバッグに多大なる時間を費やした結果、そもそもデータを取得していないことがわかったなんて失態を犯したことがあるのか、ですって。Scripting Guys がそんなことをするわけありません。一体どうして、そんなことを考えたりするのでしょうか)。

そのことを踏まえたうえでの、次のステップはというと、ローカル コンピュータの Windows Management Instrumentation (WMI) サービスにバインドして、ExecQuery メソッドを使用して、コンピュータにインストールされている全サービスに関する情報を取得することです。

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

Set colServices = objWMIService.ExecQuery ("Select * FROM Win32_Service")

ここでは WMI データ以外のデータも操作できるということをお伝えしておきましょう。お察しのとおり、Microsoft.CmdLib オブジェクトでは任意のデータを操作できます。WMI は、データ群を取得するのに便利な方法です。

サービスに関連するデータのコレクションを取得したら、そのコレクションに含まれている全サービスのデータを処理する For Each ループを設定します。ただし、ここでは、各サービスのプロパティ値をエコー バックする代わりに、次のコードを実行します。

ReDim Preserve arrResultsArray(i)
arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)
i = i + 1

このコード ブロックでは、どのような操作を行っているのか、ですって。1 行目では、配列のサイズを変更しています。ReDim Preserve コマンドを使用して、配列のサイズを変更するだけでなく、配列に格納されている既存のデータを保持しています (Preserve キーワードを指定しなくても、配列のサイズは変更できますが、その時点で配列に格納されているデータは消去されます)。

配列のサイズはどうしているのかというと、1 回目のループ処理では、配列のサイズは 0 に設定します。つまり、1 つの要素を含む配列を作成しています。配列のサイズを 0 にするという確証はどうやって得たかというと、カウンタ変数 i を使用していて、この変数の値が 0 だからです。

カウンタ変数 i を使用して配列のサイズを管理しているなら、配列のサイズが常に 0 になるのではないか、ですって。そうなるかもしれませんが、ループ処理を 1 回終えるたびに、i の値を 1 ずつ増やしているので、問題ありません。ご覧のように、この処理はコード ブロックの 3 行目で行っています。

ということで、注目する必要のあるコード行は次の 1 行だけになります。

arrResultsArray(i) = Array(objService.DisplayName, objService.State,objService.StartMode)

この行では、単に必要なプロパティ (DisplayName、State、StartMode) の値を取得して、その値を arrResultsArray 配列に追加しているだけです。ここでは、プロパティの値を個別に追加するのではなく、配列の値として追加している点に注意してください。そうですね。これは少し一般的な処理ではないですね。arrResultsArray 配列の各要素は、別の配列になっています。ですが、これは Microsoft.CmdLib オブジェクトの仕様です。

そうですね。この動作が皆さんの心配の種になる可能性がありますね。「配列の配列ですか。配列の配列に格納されている各値には、一体全体どうやってアクセスするんだ」って思っていますね。落ち着いてください。簡単ですから。Microsoft.CmdLib オブジェクトが、皆さんの代わりに、この処理を実行します。

注 : ただし、Microsoft.CmdLib オブジェクトが皆さんの代わりに実行できるのは、配列の配列に格納されている値を取得してくることだけです。意地悪な継母の部屋を掃除したり、食器を洗ったり、意地悪な異母姉妹の洋服を作ったりする作業については、このオブジェクトの次期リリースで検討します。

実際のところ、ShowResults メソッドを呼び出して、スクリプトの前半で構成したすべての配列と変数を渡すだけで、きれいな表形式でデータを出力できます。

objCmdLib.ShowResults arrHeader, arrResultsArray, arrMaxLength, strFormat, blnPrintHeader, arrBlnHide

出力結果はというと、図 1 をお見せしたときに想定していた見事に書式設定された出力です。

悪くないですよね。それほどコツコツ働かずに、すてきな外観の出力が実現できました (次のスクリプトは、最初のスクリプトに少し変更を加えるだけでよいので、さらに簡単です)。

そうですね。確かに、スクリプトの出力を表形式で表示するためにシンデレラが日夜を問わず奴隷のように働いたときに得た充足感と同じ充足感は得られないでしょう。正直なところ、王子様と結婚することもないでしょう。それは、契約外のことですから。しかし、VBScript スクリプトで、このようにすてきな出力を実現できるなら、たいした代償ではないと思います。特に、近ごろの王子様というものが、どういった人たちであるかを考えれば、納得していただけるはずです。

でも、ひょっとしたら、あなた自身の王子様にめぐり会えるかもしれません。なにしろ、王族であっても、コンピュータにインストールされている全サービスに関する情報を取得する必要がありますからね。

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

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

2008 年 6 月 : PowerShell の小道

以下の各パズルで、各文字を縦、横、斜めに一筆書きで、Windows PowerShell コマンドレットの名前を構成してください。各文字は一度しか使用できません。次に例を示します。

このパズルに含まれているコマンドレットの名前は New-Alias です。今度は、皆さんの番です。次の 3 つのパズルに挑戦してみてください。

ANSWER:

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

解答 : PowerShell の小道 (2008 年 6 月)

Read-Host

Set-AuthenticodeSignature

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

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