Scripting Guys が皆さんの質問にお答えします

 

関連情報

Hey, Scripting Guys! のその他の記事もチェックしてください。

概要: コードを記述する無償のツールを使用して、カスタムの Windows PowerShell 関数を自動的に作成する方法を紹介します。

 

Hey, Scripting Guy! Question

Scripting Guy さん、よろしくお願いします。Windows PowerShell コマンドを変更する良い方法はありませんか。コマンドのエイリアスを作成することでコマンド名を変更することはできますが、コマンドの実行する方法も変更するエイリアスを作成したいと思っています。これには、なんらかの関数を作成する必要があると思いますが、関数の作成にはかなり手間がかかります。もっと簡単に行う方法はありませんか。

-- MB

 

Hey, Scripting Guy! Answer

MB さん、こんにちは。

Microsoft Scripting Guy の Ed Wilson です。私が『Windows 7 Resource Kit』に収録するための Windows PowerShell スクリプトを作成していたときに、James Brundage が、自作の New-ScriptCmdlet 関数のコピーを私に送ってくれました。James は、New-ScriptCmdlet 関数を作成したときから、この関数について語り続けています。この関数はたいへんすばらしかったので、彼は PowerShellPack に含めることに決めました (PowerShellPack はたいへんすばらしかったので、私は Windows 7 Resource Kit に含めることにしました)。MB さんの質問のおかげで、James が New-ScriptCmdlet 関数について語る絶好の機会を得られました。

James Brundage は、自動化によって時間とお金を節約することを目的とした企業である Start-Automating (http://www.start-automating.com、英語) の設立者です。Start-Automating では、Windows PowerShell のトレーニングを実施したり、Windows PowerShell と .NET Framework のカスタム開発を行っています。James は、以前はマイクロソフトの Windows PowerShell チームで働いており、PowerShellPack (http://code.msdn.microsoft.com/PowerShellPack、英語) の作成者でもあります。PowerShellPack は、ユーザー インターフェイスの構築、RSS フィードの操作、およびシステム情報の取得を可能にする Windows PowerShell スクリプトのコレクションです。


スプラッティング (とあれこれ) シリーズ

第 2 部: スプラッティングでコマンドをラップする

今日もスプラッティング (とあれこれ) シリーズは続きます。スプラッティングの基本と使い方について解説した昨日のコラムでは、一般的な WMI クエリに、いくつかの便利なショートカットを作成しました。次の例で、記憶を呼び戻してください。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
$ComputerSystem = @{Query="Select * From Win32_ComputerSystem"}  
Get-WmiObject @ComputerSystem  

今日は、別の関数を呼び出すいくつかのラッパー関数を作成することで、これを実用化します。このコラムでは、-AsJob-ComputerName-Credential などの Get-WmiObject コマンド パラメーターはすべて保ったままで、Get-WmiObject の例を Get-ComputerSystem コマンドに変える方法を紹介します。

ラッパー関数を作成するためには、スプラッティングと、プロキシ コマンドを有効にするための組み込み変数を使用する必要があります。プロキシ コマンドは、暗黙的なリモート処理を使用するときに対応する必要がある種類のスクリプトです (Export-PSSession を参照してください)。暗黙的なリモート処理によって、ローカルに実行しているように見えるのに実際のところはリモート コンピューターに対して実行されているコマンドを使えるようになります (Get-Process を実行しているが実際にはコンピューター X にプロセスを返すコマンドなど)。このためには、ローカルのコマンドで、すべてのパラメーターをリモート コマンドに完ぺきに渡せる必要があります。それを可能にするために、Windows PowerShell 2.0 では、$psBoundParameters という変数が導入されました。

Microsoft Scripting Guys は、リモート処理について扱ったコラムを数多く執筆しています。また、ゲスト執筆者もいくつかのコラムでリモート処理を取り上げています。言うまでもなく、リモート処理は便利なのです。

変数 $psBoundParameters には、関数に渡されたすべてのパラメーターが含まれています。これには、既定の値は含まれていません (このことについて覚えておいてください。$psBoundParameters 変数は、これから何度も出てきます)。変数 $psBoundParameters をスプラッティングと共に使用すると、基本的な動作は単に別の関数を呼び出すだけだが基になる関数のすべての機能を維持した関数を作成することができます。

次のような簡単な関数を作成して、変数 $psBoundParameters が単独ではどのように機能するか見てみましょう。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
function foo($a, $b, $c = "d"{  
    $psBoundParameters  
}  

パラメーターのない foo 関数を呼び出すと、次のように何も返されません。

foo

$a$b、または $c に値を指定して foo 関数を呼び出すと、次のようにデータを含みます。

foo -a "a" -b "b" -c "c"

$a$b、または $c の値が明示的に指定されているかどうかは問題ではありません。この簡単な関数では、a、b、c は位置で指定することも可能で、変数 $psBoundParameters は問題なく機能します。次に結果を示します。

foo a b c

この変数は、どうすればスプラッティングと使用できるのでしょうか。WMI の例に話を戻しましょう。この例では、Get-WmiObject にパラメーターを 1 つだけ提供していました。次のようにクエリを提供するだけで、Get-WmiObject に用意されている他のパラメーターをどれでも使用できます。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
Get-WmiObject @ComputerSystem -ComputerName "localhost"  

コンピューターのシステム情報を取得しながら、-ComputerName-AsJob などの別のパラメーターも使用できる関数を作成する場合は、Get-WmiObject をラップするが、クエリ変数 (–Class–Namespace などの –Query パラメーターと混同するおそれがあるものすべて) は省略する関数を作成します。

PowerShellPack には、このような簡単なラッパーを簡単に記述できる New-ScriptCmdlet という便利な関数があります。

New-ScriptCmdlet 関数を使用するには、PowerShellPack をダウンロードして、モジュールをインストールし、現在の Windows PowerShell セッションにモジュールをインポートする必要があります。モジュールをインポートするには、Import-Module PowerShellPack というコマンドを使用します。

New-ScriptCmdlet 関数には、作成するコマンド名、ベースになっているコマンド、元のコマンドから取り除くパラメーター、および処理ブロックについての情報を提供する必要があります。それから、元のコマンドをラップする、新しい高度な関数 (Windows PowerShell 2.0 のベータ版では、高度な関数は ScriptCmdlet と呼ばれていました) を生成します。New-ScriptCmdlet 関数の使用例を次に示します。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
$newScriptCmdletParameters = @{  
    Name = "Get-ComputerSystem"  
    FromCommand = Get-Command Get-WmiObject  
    RemoveParameter = "Namespace""Class""Query"  
    ProcessBlock = {  
        $getWmiObjectParameters = $psBoundParameters + @{  
            Query="Select * From Win32_ComputerSystem"  
        }  
        Get-WmiObject @GetWmiObjectParameters  
    }  
}  
 
New-ScriptCmdlet @newScriptCmdletParameters  

とても便利だと思いませんか。驚くほど少ないコードで、ずっと大きな関数を作成しています。それでは、どのくらいのコードが生成されたかを知るために、簡単なパイプラインを作成してみましょう。

パイプライン自体は、Windows PowerShell の学習において楽しいものです。数行にわたってパイプライン全体を記述することもできますが、純粋な 1 つのパイプラインを作成することは、Windows PowerShell スクリプティングの楽しい練習の 1 つです。New-ScriptCmdlet の出力を変数に格納するために、結果を Set-Variable コマンドレットにパイプします。Set-Variable コマンドレットの結果を返すために、–PassThru を追加します。Set-Variable コマンドレットでは変数を返します。その変数に格納されている単語や行を数える必要があるので、変数の値が返されるように Select-Object –ExpandProperty Value を使用します。それから、これを Measure-Object –Word –Line –Character にパイプして、テキストがどれくらい生成されたかを確認します。コードは次のとおりです。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
$newScriptCmdletParameters = @{  
    Name = "Get-ComputerSystem"  
    FromCommand = Get-Command Get-WmiObject  
    RemoveParameter = "Namespace""Class""Query"  
    ProcessBlock = {  
        $getWmiObjectParameters = $psBoundParameters + @{  
            Query="Select * From Win32_ComputerSystem"  
        }  
        Get-WmiObject @GetWmiObjectParameters  
    }  
}  
 
New-ScriptCmdlet @newScriptCmdletParameters |  
    Set-Variable -Name GetComputerSystem -PassThru |  
    Select-Object -ExpandProperty Value |  
    Measure-Object -Word -Line -Character  

驚くべきことに、約 15 行のコードで、100 行以上の結果を生成していました。これは、かなりの時間の節約になります。

New-ScriptCmdlet ではテキストを生成するので、これから使用する関数に変換するためには、New-ScriptCmdletSet-Content にパイプしてファイルをドット ソース形式で読み込んで、関数をインポートします。また、データを含む別のスクリプト ブロックを宣言してスクリプト ブロックをドット ソース形式で読み込むこともできます。皆さんは、おそらく最初の手法の方がなじみがあり、2 つ目の手法にはあまりなじみがないと思いますので、どちらも実際にお見せします。

前の例では、$GetComputerSystem という変数を作成するために Set-Variable コマンドレットを使用しました。次に、Windows PowerShell スクリプト ファイルに、この変数のコンテンツを出力し、スクリプト ファイルをドット ソース形式で読み込んで、それから新しくインポートされた Get-ComputerSystem 関数を使用する例を示します。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
$GetComputerSystem |   
    Set-Content .\Get-ComputerSystem.ps1  
 
. .\Get-ComputerSystem.ps1  
 
Get-ComputerSystem  
 

scriptblock クラスの create メソッドを使用して動的に関数を作成している例を次に示します。scriptblock クラスは、.NET Framework 名前空間の System.Management.Automation に含まれています。幸運にも、Windows PowerShell チームは、このクラスを簡単に使用するための [scriptblock] 型アクセラレーターを作成しています。コードは次のとおりです。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
. ([ScriptBlock]::Create($GetComputerSystem))  
Get-ComputerSystem  

次の画像を見るとわかるように、出力は Get-WmiObject Win32_ComputerSystem と同じように見えますが (これは、実際にそのとおりだからです)、より便利なコマンドで出力を参照できるようになりました。

出力の画像

このような簡単なラッパーを、あらゆる種類のケースに合わせて作成することができます。どちらの手法も、お好みの関数を作成するのに使用できます。動的な手法は、ちょっと試してみる場合には最適ですが、運用コンピューターで使用する場合は New-ScriptCmdlet を読み込む必要があるため、個人的に、運用環境で作業するときには最初の手法を使用しています。

コマンドをラップする方法についての説明は以上です。おさらいのため、パフォーマンス カウンター用のスクリプトを作成します。コードは次のとおりです。

PowerShell
スクリプトの編集|{#scriptcode_dlg.remove_script}
# New-ScriptCmdlet のパラメーター セットを作成します   
$newScriptCmdletParameters = @{  
    # コマンド名は Watch-ProcessorPerformance です         
    Name = "Watch-ProcessorPerformance"                     
    # これは Get-Counter に基づいています        
    FromCommand = Get-Command Get-Counter  
    # ListSet パラメーターと Conter パラメーターを削除します  
    RemoveParameter = "ListSet""Counter"  
    # コマンドの処理ブロックは、パラメーターを指定して単に Get-Counter を呼び出します  
    ProcessBlock = {  
        $getCounterParameters= $psBoundParameters + @{  
            Counter='\Processor(_total)\% Processor Time'  
        }  
        Get-Counter @getCounterParameters  
    }  
}  
 
# New-ScriptCmdlet を実行して、値を変数に格納します  
New-ScriptCmdlet @newScriptCmdletParameters |  
    Set-Variable -Name GetCounter  
      
# その変数のあるスクリプト ブロックを作成して、ドット ソース形式で読み込みます (インポートします)  
.([ScriptBlock]::Create($GetCounter))  
 
# Watch-ProcessorPerformance コマンドを呼び出します  
Watch-ProcessorPerformance -MaxSamples 3

WMI やパフォーマンス カウンターから取得する情報は、これよりもずっと複雑になることがありますが、この手法は、別のコマンドをラップするコマンドを作成するときは必ず役に立ちます。

無数の汎用コマンドレットを、必要なツールに変換する最適な手法の 1 つとして、スプラッティング (と、スプラッティングに関連するあれこれ) は、信じられないほど膨大な数の扉を開きます。スプラッティングによって、必要なツールを発見して使用するのが、非常に簡単になります。また、Get-Counter ‘\Processor(_total)\% Processor Time’ よりも Watch-ProcessorPerformance の方が簡単に覚えられるのも事実です。

今日のコラムでは、単一のコマンドレットの便利な表現を作成する方法を紹介しました。次のコラムでは、2 つのコマンドをつなげる方法について紹介します。

MB さん、スプラッティングを使用して、より複雑な Windows PowerShell コマンドをラップする方法についてお伝えできることは、これで全部です。ゲスト執筆者週間は明日も続きます。明日は、James が、スプラッティングとコマンドのメタデータについて説明します。

ぜひ、私たちの Twitter (英語)Facebook (英語) をチェックしてください。質問がある場合は、scripter@microsoft.com (英語のみ) 宛てに電子メールをお送りいただくか、公式の Scripting Guys フォーラム (英語) に質問をご投稿ください。それでは、また明日。それまでの間、しばしのお別れです。

Scripting Guys (Ed Wilson、Craig Liebendorfer)

ページのトップへ