Hey, Scripting Guy!自己文書化スクリプトを作成する

Microsoft Scripting Guys

ここ、ブエノスアイレスの深みのある青空の色を言い表すには、特別な色の名前を使う必要があります。コバルト ブルー、ディープシー ブルー、ミッドナイト ブルー、サファイア ブルーなど、特殊な色の名前はありますが、どの色名も、この古都を覆う空の実際の色の描写することはできません。数百年もの歴史ある建物に沿った幅広い歩道には、小さいテーブルが点々と置かれています。私は空いていた金属製のいすに腰を掛けています。良い香りのそよ風が気だるく吹き付け、シナモン、カルダモン、およびタイムの香りがかすかに漂ってきます。カモメは翼を広げて上昇気流に乗り、その巧みさ、優雅さ、および身のこなしは、ビーチ 1 番のサーファーもうらやむほどです。カフェ コン レチェ (スペイン語で「カフェ オレ」のこと) が非常にふさわしく感じられ、ブエノスアイレス市街の景色や音に引けを取らないほど濃い味です。私は、マイクロソフト プレミア サポートを契約している顧客と VBScript を使用してネットワークを管理する方法について話し合うために、この街を訪れています。私はカメラ バッグから折りたたんだ地図を取り出して、Calle Florida から Nueve de Julio 通りのオベリスクまでの行き方を練ります。

私は地図で現在地を見つけます。Galerias Pacifico ショッピング ギャラリーの近くです。地図を現在の時間と空間に合わせようと、私は地図を何度もひっくり返します。ちょうどこのとき、長身で目立つ容姿の、スリーピースのダーク スーツを着た男性が現れ、日差しがちょうど当たらない場所に立ちます。彼の影が地図の上でいたずらっぽく踊るので、私は顔を上げます。彼が「ブエノス ディアス、セニョール」と言ったので、私は立ち上がります。

「ブエノス ディアス」と私は律義に返します。

「もしかして、アメリカの方ですか」と彼は少し英国なまりの口調で答えます。

私はいささか面食らって、そのとおりだとためらいながら答えます。

彼は「私たちのすてきな街を訪れるのは初めてですか」とたずねます。

私は再び遠慮がちに「そうです」と言います。

「では、ささやかなお手伝いができるかもしれません。何をお探しですか」と、彼は礼儀正しくたずねます。

私が彼に行き先を告げると、彼は道順を教えてくれます。その後、私たちはしばらくおしゃべりをします。

「すごいぞ」と私は思います。「自己文書化都市だ。ここでは地図は要らないな。住人がとても親切だから、地図を見る間もなくだれかが手伝いに来てくれるんだ」

実は、これは数年前の出来事でした。現在、ノースカロライナの自宅では外の天気があまりにすてきなので、ブエノスアイレスへの旅、そして自己文書化機能のすばらしさが思い起こされます。事前に計画する時間を割けば、同様のサービスをスクリプトで実行できます。このコラムでは、一定のパターンに従う他のスクリプトからコメントを収集するスクリプトの作成方法について説明します。各自のニーズに合わせてパターンを変更したり、この技法を拡張して文書作成以外のスクリプトに使用したりすることができます。

ていねいな説明が記述されたスクリプトを見てみましょう。図 1 の GetSetIEStartPage.ps1 スクリプト (Hey, Scripting Guy! の記事「Internet Explorer のホーム ページを変更する方法はありますか」のために作成されたスクリプト) では、here-string 内にコメントが記述され、この here-string が $Comment という名前の変数に代入されています。here-string はアット マークと引用符の開始タグ (@")、および引用符とアット マークの終了タグ ("@) で区切られています。取得対象のコメント ブロックは 3 つあります。最初のコメント ブロックには、一般的なスクリプト ヘッダー情報 (タイトル、作成者、日付、キーワード、およびスクリプト自体についてのコメント) が記述されています。2 つ目と 3 つ目のコメント ブロックは、スクリプトに含まれる 2 つの主要な関数に関連しています。

図 1 GetSetIEStartPage.ps1

Param([switch]$get,[switch]$set,$computer="localhost")
$Comment = @"
NAME: GetSetIEStartPage.ps1
AUTHOR: ed wilson, Microsoft
DATE: 1/5/2009

KEYWORDS: stdregprov, ie, [wmiclass] type accelerator,
Hey Scripting Guy
COMMENTS: This script uses the wmiclass type accelerator
and the stdregprov to get the ie start pages and to set the
ie start pages. Using ie 7 or better you can have multiple
start pages

"@ #end comment

Function Get-ieStartPage()
{
$Comment = @"
FUNCTION: Get-ieStartPage 
Is used to retrieve the current settings for Internet Explorer 7 and greater.
The value of $hkcu is set to a constant value from the SDK that points
to the Hkey_Current_User. Two methods are used to read
from the registry because the start page is single valued and
the second start pages key is multi-valued.

"@ #end comment
 $hkcu = 2147483649
 $key = "Software\Microsoft\Internet Explorer\Main"
 $property = "Start Page"
 $property2 = "Secondary Start Pages"
 $wmi = [wmiclass]"\\$computer\root\default:stdRegProv"
 ($wmi.GetStringValue($hkcu,$key,$property)).sValue
 ($wmi.GetMultiStringValue($hkcu,$key, $property2)).sValue
} #end Get-ieStartPage

Function Set-ieStartPage()
{
$Comment = @"
FUNCTION: Set-ieStartPage 
Allows you to configure one or more home pages for IE 7 and greater. 
The $aryValues and the $Value variables hold the various home pages.
Specify the complete URL ex: "http://www.ScriptingGuys.Com" make sure
to include the quotation marks around each URL. 

"@ #end comment
  $hkcu = 2147483649
  $key = "Software\Microsoft\Internet Explorer\Main"
  $property = "Start Page"
  $property2 = "Secondary Start Pages"
  $value = "https://www.microsoft.com/technet/scriptcenter/default.mspx"
  $aryValues = "https://social.technet.microsoft.com/Forums/en/ITCG/threads/",
  "https://www.microsoft.com/technet/scriptcenter/resources/qanda/all.mspx"
  $wmi = [wmiclass]"\\$computer\root\default:stdRegProv"
  $rtn = $wmi.SetStringValue($hkcu,$key,$property,$value)
  $rtn2 = $wmi.SetMultiStringValue($hkcu,$key,$property2,$aryValues)
  "Setting $property returned $($rtn.returnvalue)"
  "Setting $property2 returned $($rtn2.returnvalue)"
} #end Set-ieStartPage

# *** entry point to script 
if($get) {Get-ieStartpage}
if($set){Set-ieStartPage}

ソース ファイルのコメントを別のファイルに書き込むには、元のスクリプト ファイルを開いてコメントを探し、適切なテキストを新しいファイルに書き込む必要があります。とても簡単そうですね。それから、新しいスクリプトの名前も必要なので、GetCommentsFromScript.ps1 と呼ぶことにしましょう。

GetCommentsFromScript.ps1 スクリプト (図 2 参照) は、Param ステートメントで始まります。Param ステートメントは、実行時にスクリプトに情報を指定できるようにするために使用します。以下に、Param ステートメントを示します。

図 2 GetCommentsFromScript.ps1

Param($Script= $(throw "The path to a script is required."))
Function Get-FileName($Script)
{
 $OutPutPath = [io.path]::GetTempPath()
 Join-Path -path $OutPutPath -child "$(Split-Path $script-leaf).txt"
} #end Get-FileName

Function Remove-OutPutFile($OutPutFile)
{
  if(Test-Path -path $OutPutFile) { Remove-    Item $OutPutFile | Out-Null }
} #end Remove-OutPutFile

Function Get-Comments($Script,$OutPutFile)
{
 Get-Content -path $Script |
 Foreach-Object `
  { 
    If($_ -match '^\$comment\s?=\s?@"')
     { 
      $beginComment = $True 
     } #end if match @"
   If($_ -match '"@')
     { 
      $beginComment = $False
     } #end if match "@
   If($beginComment -AND $_ -notmatch '@"') 
     {
      $_ | Out-File -FilePath $OutPutFile -append
     } # end if beginComment
  } #end Foreach
} #end Get-Comments

Function Get-OutPutFile($OutPutFile)
{
 Notepad $OutPutFile
} #end Get-OutPutFile

# *** Entry point to script ***
$OutPutFile = Get-FileName($script)
Remove-OutPutFile($OutPutFile)
Get-Comments -script $script -outputfile $OutPutFile
Get-OutPutFile($OutPutFile)vw

Param($Script= $(throw "The path to a script   is required."))

コマンド ライン パラメータを使用するメリットは、コピーするコメントが含まれるスクリプトへのパスを指定するために、対象のスクリプト ファイルを開いて編集する必要がないことです。$Script 変数に既定値を代入して、このパラメータを必須にしています。既定値では throw コマンドを使用してエラーを発生させているので、–script パラメータに値を指定しない限り、スクリプトでは必ずエラーが発生します。

少し本題から離れて、図 3 の DemoThrow.ps1 スクリプトで throw ステートメントがどのように使用されているかを見てみましょう。Set-Error 関数の throw ステートメントによって発生するエラーを回避するには、まず $errorActionPreference 変数に SilentlyContinue を設定する必要があります。この設定によって、エラーが表示されなくなり、スクリプトを続行できるようになります。これは、VBScript の On Error Resume Next 設定と同じです。If ステートメントは、$value 変数の値を評価するために使用します。条件に一致する場合は、throw ステートメントが実行され、例外がスローされます。エラーの評価には、Get-ErrorDetails 関数を使用しています。Get-ErrorDetails 関数の最初の処理は、エラー数の表示です。throw ステートメントによって発生したエラーにより、エラー数は 1 増加します。続いて、最初のエラーを取得して (インデックス値が 0 のエラーは最新のエラーです)、エラー オブジェクトを Format-List コマンドレットに渡します。すべてのプロパティを選択します。ただし、呼び出し情報はオブジェクトとして返されるので、返されるオブジェクトに対して直接クエリを実行する必要があります。これを行うには、エラー オブジェクトの InvocationInfo プロパティを使用して呼び出しオブジェクトにアクセスします。表示されるエラー情報を図 4 に示します。

図 3 DemoThrow.ps1

Function Set-Error
{
 $errorActionPreference = "SilentlyContinue"
 "Before the throw statement: $($error.count) errors"
 $value = "bad"
 If ($value -eq "bad") 
   { throw "The value is bad" }
} #end Set-Error

Function Get-ErrorDetails
{
 "After the throw statement: $($error.count) errors"
 "Error details:"
 $error[0] | Format-List -Property * 
 "Invocation information:"
 $error[0].InvocationInfo
} #end Get-ErrorDetails

# *** Entry Point to Script
Set-Error

Get-ErrorDetails

fig04.gif

図 4 エラーを発生させるための Throw ステートメントの使用

では、今回の本題のスクリプトである GetCommentsFromScript.ps1 に戻りましょう。スクリプトから収集したすべてのコメントが書き込まれる新しいテキスト ファイルの名前を作成する関数が必要です。そのためには、Function キーワードを使用し、キーワードの後に関数名を指定します。Windows PowerShell の動詞と名前で構成される名前付け規則に従って、この関数に Get-FileName という名前を付けました。Get-FileName 関数では、解析対象のスクリプトのパスを表す入力パラメータを 1 つ受け取ります。このパスは関数内の $Script 変数で保持されます。Get-FileName 関数の開始部分は、次のようになります。

Function Get-FileName($Script)
{

次に、ローカル コンピュータ上の一時フォルダのパスを取得します。環境変数による Windows PowerShell ドライブの使用など、パスの取得方法は多数あります。しかし、今回は Io.Path という .NET Framework クラスの静的な GetTempPath メソッドを使用することにしました。GetTempPath メソッドでは一時フォルダのパスを返します。このフォルダに、新しく作成したテキスト ファイルを格納します。次のように、一時フォルダのパスを $OutPutPath 変数に格納します。

$OutPutPath = [io.path]::GetTempPath()

新しいテキスト ファイルの名前は、スクリプトの名前にちなんで付けることにしましょう。そのためには、スクリプトが格納されているパスからスクリプト名を分離する必要があります。この外科手術を実行するには、Split-Path コマンドレットを使用します。–leaf パラメータにより、コマンドレットにスクリプト名を返すよう指示しています。スクリプトが格納されているディレクトリのパスが必要な場合は、–parent パラメータを使用します。この処理を最初に実行する必要があるので、Split-Path コマンドをかっこで囲みます。かっこの前にドル記号を記述し、コードを実行してスクリプトの名前を返す部分式を作成します。テキスト ファイルの拡張子に .ps1 を使用することもできますが、スクリプトの拡張子と同じなので紛らわしくなる可能性があります。このため、返されたファイル名には、単に .txt という拡張子を追加し、全体を引用符で囲みます。今度は Join-Path コマンドレットを使用して出力ファイルの新しいパスを作成します。新しいパスは、$OutPutPath 変数に格納された一時フォルダと Split-Path コマンドレットを使用して作成したファイル名で構成されます。文字列の操作と連結を使用して新しいファイルのパスを作成することもできますが、このような処理を実行するには Join-Path コマンドレットと Split-Path コマンドレットを使用する方がずっと信頼性が高くなります。コードは次のようになります。

Join-Path -path $OutPutPath -child "$(Split-Path $script-leaf).txt"
} #end Get-FileName

ここで、重複ファイルの処理方法を決める必要があります。次のようなコードを使用すると、重複ファイルが存在することを知らせてユーザーに確認することができます。

$Response = Read-Host -Prompt "$OutPutFile already exists. Do you wish to delete it <y / n>?"
if($Response -eq "y")
    { Remove-Item $OutPutFile | Out-Null }
ELSE { "Exiting now." ; exit }

既存のファイルの拡張子を .old に変更して、ファイルのバックアップを作成するという名前付けアルゴリズムの一種を実装することもできます。このアルゴリズムを実装すると、コードは次のようになります。

if(Test-Path -path "$OutPutFile.old") { Remove-Item-Path "$OutPutFile.old" }
Rename-Item -path $OutPutFile -newname "$(Split-Path $OutPutFile -leaf).old"

また、単に既存のファイルを削除することもできます。今回はこの方法を採用しました。実行する処理は、Remove-OutPutFile 関数で行われます。Remove-OutPutFile 関数を作成するには、Function キーワードを使用して関数の名前を指定します。次のように、$OutPutFile 変数を使用して関数への入力を指定します。

Function Remove-OutPutFile($OutPutFile)
{

ファイルが存在するかどうか確認するには、Test-Path コマンドレットを使用して $OutPutFile 変数に格納されている文字列を path パラメータに指定します。Test-Path コマンドレットでは、ファイルが見つかったかどうかに応じて True または False のどちらかだけを返します。したがって、If ステートメントを使用してファイルの存在を評価できます。ファイルが見つかった場合は、スクリプト ブロックの処理を実行します。ファイルが見つからなかった場合は、スクリプト ブロックの処理は実行しません。次のように、1 つ目のコマンドではファイルが見つからず、False が返されます。2 つ目のコマンドでは、ファイルが見つからなかったのでスクリプト ブロックは実行されません。

PS C:\> Test-Path c:\missingfile.txt
False
PS C:\> if(Test-Path c:\missingfile.txt){"found file"}
PS C:\>

Remove-OutPutFile 関数内では、If ステートメントを使用して、$OutPutFile 変数で参照しているファイルが既に存在しているかどうかを確認します。ファイルが存在する場合は、Remove-Item コマンドレットを使用してそのファイルを削除します。ファイルが削除されたときに通常返される情報は、パイプラインを使用して Out-Null コマンドレットに渡され、画面に表示されずに処理されます。コードは次のようになります。

if(Test-Path -path $OutPutFile) { Remove-Item $OutPutFile | Out-Null }
} #end Remove-OutPutFile

出力ファイルの名前を作成し、以前の出力ファイルが存在する場合はそのファイルを削除したら、今度はスクリプトからコメントを取得します。コメントを取得するには、次のように、Get-Comments 関数を作成し、$Script 変数と $OutPutFile 変数の両方を渡します。

Function Get-Comments($Script,$OutPutFile)
{

ここで、Get-Content コマンドレットにスクリプトのパスを渡してスクリプトのテキストを読み取ります。Get-Content コマンドレットを使用してファイルを読み取ると、ファイルのデータが 1 行ずつ読み取られ、各行がパイプされます。読み取った結果を変数に格納すれば、配列になります。次のように、$a 変数は他の任意の配列と同様に操作できます。たとえば、Length プロパティを使用して配列の要素数を取得したり、次のように配列に直接インデックスを作成したりできます。

PS C:\fso> $a = Get-Content -Path C:\fso\  GetSetieStartPage.ps1
PS C:\fso> $a.Length
62
PS C:\fso> $a[32]
($wmi.GetMultiStringValue($hkcu,$key,   $property2)).sValue

入力スクリプトを読み取ってパイプラインに渡すコード行を以下に示します。

Get-Content -path $Script |

今度は、各行の内容を調べて、コメント ブロックに含まれる行かどうかを確認します。パイプラインの各行を調べるには、ForEach-Object コマンドレットを使用します。コレクション内の個別のオブジェクトを 1 つずつ処理できるようにするという点で、このコマンドレットは ForEach ~ Next ステートメントと似ています。アクサン グラーブ文字 (`) は、次の行にコマンドを継続するために使用します。各オブジェクトがパイプラインに渡されるたびに実行する必要がある処理は、1 組の中かっこで囲んでスクリプト ブロック内に記述します。Get-Content 関数のこの部分は、次のようになります。

ForEach-Object `
  { 

ForEach-Object コマンドレットの処理ブロックでは、テキスト行を調べます。そのためには、If ステートメントを使用します。$_ 自動変数は、パイプラインに存在する現在の行を表すために使用します。–match 演算子を使用して、テキスト行が正規表現のパターンに一致するかどうかを確認します。結果に応じて、–match 演算子は True または False のブール値を返します。パターンは、–match 演算子の右側から始まります。スクリプトのこのセクションは次のようになります。

PS C:\fso> '$Comment = @"' -match '^\$comment\s?=\s?@"'
True

このスクリプトで使用している正規表現パターンは、次のような多数の特殊文字で構成されています。

^ 先頭に一致します。

\ ドル記号がリテラル文字として扱われ、正規表現の特殊文字として扱われないようにするためのエスケープ文字。

$comment リテラル文字。

\s? 0 文字以上の空白文字。

= リテラル文字。

\s? 0 文字以上の空白文字。

@" リテラル文字。

パイプラインに存在するテキストの現在の行を調べるコードのセクションは、次のようになります。

If($_ -match '^\$comment\s?=\s?@"')

$beginComment という名前の変数を作成し、この変数を使用してコメント ブロックの先頭をマークします。–match ステートメントに一致するものが見つかった場合は、コメント ブロックの先頭が見つかったことになります。次のように、変数に $True を設定します。

{ 
  $beginComment = $True
} #end if match @"

次に、コメント ブロックの末尾かどうかを確認します。そのためには、再び –match ステートメントを使用します。今度は、"@ という文字の並びを検索します。これは here-string を終了するために使用している文字列です。"@ が見つかったら、$beginComment 変数に $False を設定します。

If($_ -match '"@')
     { 
      $beginComment = $False
     } #end if match "@

これで最初の 2 つの If ステートメントが実行されました。最初の If ステートメントでは here-string の先頭を特定し、次の If ステートメントでは here-string の末尾を特定しています。今度は、コメント ファイルに書き込むテキストを取得します。そのためには、$beginComment 変数に True が設定されている必要があります。また、アット マークと引用符を組み合わせた (@") 文字は here-string の末尾を表すので、この文字が行内に存在しないことも確認する必要があります。この判断を行うには、次のように複合的な If ステートメントを使用します。

If($beginComment -AND $_ -notmatch '@"') 
     {

ここで、テキストを出力ファイルに書き込みます。そのためには、$_ 自動変数 (テキストの現在の行を表します) を使用し、この変数を Out-File コマンドレットにパイプします。Out-File コマンドレットは、コメント ファイルのパスが格納されている $OutPutFile 変数を受け取ります。–append パラメータを使用して、コメント ファイルにスクリプトのコメントをすべて収集するよう指定します。Out-File コマンドレットでは、既定で既存の内容が上書きされるので、–append パラメータを使用しないと、テキスト ファイルには最後のコメントだけが書き込まれます。続いて、すべての中かっこを閉じます。それぞれの終わり中かっこの後に中かっこの目的を説明するコメントを追加することが、ベスト プラクティスだと私は考えています。このようにすると、スクリプトが数段読みやすくなり、トラブルシューティングや管理も簡単になります。

$_ | Out-File -FilePath $OutPutFile -append
     } # end if beginComment
  } #end ForEach
} #end Get-Comments

今度は、ユーザーが内容を確認できるように出力ファイルを開く、Get-OutPutFile という名前の関数を作成します。一時フォルダは見つけにくく、$OutPutFile 変数にファイルのパスを格納しているので、スクリプトを使用して出力ファイルを開くのは理にかなっています。Get-OutPutFile 関数は $OutPutFile という入力変数を 1 つ受け取ります。$OutPutFile 変数には、開く対象となるコメント ファイルのパスが格納されています。Get-OutPutFile 関数を呼び出すときに、$OutPutFile 変数を関数に渡します。Get-OutPutFile 関数には任意の値を渡すことができ、渡した値は関数内で $OutPutFile 変数で参照されます。文字列を直接 (文字列を引用符で囲まないで) 関数に渡すこともできます。

Function Get-OutPutFile($OutPutFile)
{
 Notepad $OutPutFile
} #end Get-OutPutFile

Get-OutPutFile -outputfile C:\fso\GetSetieStartPage.ps1

一般に、スクリプトを作成する場合、何かを収集して関数に渡すときは、関数の内側と外側の両方で使用される同一の変数名でデータを表すことをお勧めします。これは、"スクリプトの実際の処理を行うセクションには手を加えない" というスクリプト開発のベスト プラクティスの 1 つに従っています。この例では、関数を呼び出す箇所で "実際の処理" を行っています。今後、このセクションを変更する際には、文字列リテラル値の変更が必要になる可能性があります。文字列を変数に格納すれば、変数の値を簡単に変更できます。実際、今回のスクリプトでは、コマンド ラインを使用するか、他の関数による処理を使用して変数の値を指定するようにしています。できる限り、文字列リテラル値を直接スクリプトに記述することは避けてください。以下のコードでは、変数を使用して、Get-OutPutFile 関数に渡すファイルのパスを保持しています。

Function Get-OutPutFile($OutPutFile)
{
 Notepad $OutPutFile
} #end Get-OutPutFile

$OutPutFile = "C:\fso\GetSetieStartPage.ps1"
Get-OutPutFile -outputfile $OutPutFile

Get-OutPutFile 関数のコードを以下に示します。

Function Get-OutPutFile($OutPutFile)
{
 Notepad $OutPutFile
} #end Get-OutPutFile

出力ファイルのパスを表す文字列リテラルを入力する代わりに、$OutPutFile 変数で Get-FileName 関数によって作成されるパスを受け取ります。Get-FileName 関数では、抽出するコメントが含まれたスクリプトのパスを受け取ります。このスクリプトのパスは、コマンド ライン パラメータで渡されます。関数の入力パラメータが 1 つの場合は、かっこを使用して関数に入力パラメータを渡すことができます。一方、関数で 2 つ以上の入力パラメータが使用されている場合は、パラメータ名の前にハイフンを記述する形式で入力パラメータを指定する必要があります。

$OutPutFile = Get-FileName($script)

次に、前述の Remove-OutPutFile 関数を呼び出し、以下のようにこの関数に $OutPutFile 変数に格納されている出力ファイルのパスを渡します。

Remove-OutPutFile($OutPutFile)

出力ファイルの名前を確認したら、Get-Comments 関数を呼び出して、$script 変数で指定されているパスのスクリプトからコメントを取得します。コメントは $OutPutFile 変数で参照されている出力ファイルに書き込まれます。このコード行を以下に示します。

Get-Comments -script $script -outputfile $OutPutFile

コメントがすべて出力ファイルに書き込まれたら、最後に Get-OutPutFile 関数を呼び出して、$OutPutFile 変数に格納されているパスを渡します。コメント ファイルを表示しない場合は、この行をコメント アウトするか、この行と Get-OutPutFile 関数自体をスクリプトから削除できます。保存する前に各ファイルの内容を確認するには、以下のようにコード行をそのままにします。

Get-OutPutFile($OutPutFile)

GetCommentsFromScript.ps1 スクリプトを実行する際、画面上に確認メッセージは表示されません。スクリプトが動作した証拠となるのは、メモ帳で表示される新しく作成されたテキスト ファイルの存在だけです (図 5 参照)。

fig05.gif

図 5 メモ帳で表示された新しいテキスト ファイル

GetCommentsFromScript.ps1 スクリプトは、皆さんのスクリプトの作成方法に合わせて簡単に変更でき、テキスト ベースのログ ファイルから他の種類の情報を収集することもできます。必要な作業は、テキストの収集対象となる部分の先頭と末尾をマークする正規表現パターンを変更することだけです。このスクリプトを楽しんでいただければさいわいです。また、スクリプト センターでは平日は毎日新しい Hey, Scripting Guy! コラムを公開しているので、ぜひアクセスしてください。

Ed Wilson は有名なスクリプトの専門家です。『Windows PowerShell Scripting Guide』(Microsoft Press、2008 年)、『Microsoft Windows PowerShell Step by Step』(Microsoft Press、2007 年) など、8 冊の書籍を執筆しています。Ed は、マイクロソフト認定システム エンジニア (MCSE) や情報システム セキュリティ プロフェッショナル (CISSP) など、20 種類を超えるこの業界の資格を持っています。また、余暇には、木工作業、水中写真撮影、スキューバ ダイビング、お茶などを楽しんでいます。

Craig Liebendorfer は言葉を巧みに操る、マイクロソフトのベテラン Web 編集者です。Craig は毎日言葉にかかわって給料を受け取ることができる仕事が存在することが、いまだに信じられないと思っています。彼が大好きなことの 1 つは場違いなユーモアなので、まさにこのコラムにうってつけの人物です。彼は自分の立派な娘が、自分の人生における最も偉大な功績であると考えています。