Windows PowerShell: Windows PowerShell の食べられないシリアル
シリアル化は、Windows PowerShell でオブジェクトをエクスポートしたり取得したりする際に便利な機能です。
Don Jones
Windows PowerShell について講義をするときは、次のように、Get-Member コマンドレットにオブジェクトをパイプするよう常に教えています。
Get-Process | Get-Member
Get-Member (GM) コマンドレットは、Microsoft .NET Framework のリフレクションという機能を使用するように設計されています。これは、正式名、プロパティ、メソッドなど、オブジェクトについての情報を表示する機能です。このコマンドレットを使用すると、検索エンジンで探し回ったり、MSDN ライブラリの Web サイトを熟読したりするよりも、ずっと簡単にオブジェクトの役割を調べられます。
ですが、受講生がこのコマンドレットを実行すると、Get-Member コマンドレットから次のような結果が返されることがあります。
TypeName: Deserialized.System.Diagnostics.Process
Process が何かはわかりますが、"Deserialized (シリアル化が解除された)" とは何でしょうか。この Get-Member コマンドレットの出力をさらに詳しく見てみると、プロセスを停止して別の操作を開始するメソッドがある普通のプロセスとは異なり、シリアル化が解除されたプロセスにはメソッドが存在しないようです。つまり、何の操作も実行できないということです。これはどういうことでしょう。
オブジェクトがシリアル化される方法
Windows では、オブジェクトはソフトウェアの一部を機能させる役割を担っています。プロセスは、文字どおりソフトウェア アプリケーションです。プロセス オブジェクトでは、名前、メモリの使用量など、プロセスのいくつかの属性を表すプロパティが提供されます。また、プロセス オブジェクトには、停止、更新などの動作をトリガーするメソッドが用意されている場合もあります。
オブジェクトは、コンピューター上で使用する分には問題ありませんが、オブジェクト全体をネットワーク経由で転送する実用的な方法はありません。シリアル化は、オブジェクトをテキストで表す方法です (多くの場合 XML が使用されます)。図 1 は、XML にシリアル化されたプロセス オブジェクトです。
基本的に、シリアル化の手法では、オブジェクトのプロパティのスナップショットを作成して、プロパティを構造化した XML ファイルにエンコードし、その XML ファイルをネットワーク経由で転送するというものです。この時点では、XML ファイルは基本的に単なるテキスト ファイルです。実際の実行プロセスとその XML ファイルの間に、直接的なつながりはありません。XML ファイルは、転送されるオブジェクトのプロパティの、ある特定の時点の状態を表しています。
図 1 XML ファイルによってシリアル化されたプロセス オブジェクト
シリアル化では、オブジェクトのメソッドが維持されないので、元のオブジェクトに戻して、メソッドを実行するように指定することはできません。シリアル化が解除されるのは、シリアル化されたオブジェクトをシェルが読み取る必要があるときです。シリアル化の解除は、XML のテキストを読み取って、元のオブジェクトとよく似たものを構築することで行われます。もちろん、メソッドは含まれていません。
シリアル化が行われる一般的なケース
Windows PowerShell 2.0 では、次の場合に一般的にシリアル化が行われます。
- Export-CliXML コマンドレットを使用してオブジェクトを XML 形式でエクスポートするとき
- Windows PowerShell リモート処理を使用して、リモート コンピューターからオブジェクトを取得するとき
たとえば、次のコマンドを使用すると、実行中のプロセスをリモート コンピューターから取得し、仮想メモリの使用量で並べ替えて、上位 10 個を表示できます。
invoke-command { ps } -computer server-r2 | sort vm -desc | select -first 10
また、次のように、すべての並べ替えと選択処理をリモート コンピューターで実行することも可能です。リモート コンピューターで処理を実行すると、ネットワーク経由で転送するシリアル化されたオブジェクトの数が少なくなるというメリットがあります。
invoke-command { ps | sort vm -desc | select -first 10 } -computer server-r2
重要なのは、ネットワーク経由で転送するオブジェクトは実際のプロセスではなくなっているということです。オブジェクトは XML 形式になっています。ローカル コンピューターで実行中のプロセスとリモート コンピューターで実行中のプロセスの間にはつながりがないため、プロセスを 1 つ取得して、停止するように指定することはできません。
シリアル化は、オブジェクトの情報を格納するのにも便利です。たとえば、次のようにすべてのサービスの構成を XML ファイルにエクスポートするとしましょう。
get-wmiobject win32_service | export-clixml baseline.xml
今後、このスナップショットは、サーバーの構成を比較する際に使用できます。この情報により、意図するとせざるとにかかわらず構成が変更されたときに注意することができます。
次のコマンドは、現在のサービス オブジェクトとスナップショットにあるサービス オブジェクトを比較します。
compare-object (get-wmiobject win32_service) (import-clixml baseline.xml)
この例では、シリアル化を解除したオブジェクトにメソッドがないという事実は重要ではありません。というのも、起動モードやログイン アカウントなどの構成の情報がすべて含まれているのは、プロパティだからです。
シリアル化されたオブジェクトに関する注意点
ところが、オブジェクトのメソッドにアクセスする必要がある場合、シリアル化によって問題が発生します。たとえば次のコマンドは、ローカル コンピューターでは問題なく機能します (実行するとコンピューターが再起動されるので、再起動してもかまわない場合にのみ実行してください)。
Get-WmiObject Win32_OperatingSystem | ForEach-Object { $_.Reboot() }
ところが、次のコマンドはうまく機能しません。
Invoke-Command { Get-WmiObject Win32_OperatingSystem } –computer Server-R2 | ForEach-Object { $_.Reboot() }
これは、Invoke-Command コマンドレットの結果が、メソッドを持たない、シリアル化を解除されたオブジェクトだからです。このため、Reboot メソッドを実行できません。ですが、次のようにすれば問題は解決します。
Invoke-Command { Get-WmiObject Win32_OperatingSystem | ForEach-Object { $_.Reboot() }} –computer Server-R2
オブジェクトがシリアル化されてメソッドが存在しなくなる前に、リモート コンピューターで Reboot メソッドを呼び出しています。このように、シリアル化が問題を引き起こすように見えても、たいていの場合は回避策があります。シリアル化が行われるタイミングを把握しておくだけでかまいません。
Don Jones は、Concentrated Technology の創設者で、ConcentratedTech.com (英語) で Windows PowerShell や他のテクノロジに関する質問に答えています。また、Nexus.Realtimepublishers.com (英語) の創設者でもあり、このサイトでは、彼の多くの著書が無料でオンライン ブックとして提供されています。