コマンドレットのパラメータを列挙してみる。|Tweet昨日の記事で取り上げたJavaScriptSerializerを用いると、連想配列から楽にJSONを作成できることが分かったのですが、この記事を書いていて思ったのは、「PowerShellの連想配列って意外に使えるな」という点でした。現在のところ、PowerShellは独自のクラスを記述する方法がありません(Add-Typeコマンドレットを用いてC#などでクラスを書いて利用することはできますが)。Add-Memberコマンドレットを用いると、既存のオブジェクトに対し、任意のプロパティやPowerShellスクリプトで記述したメソッドを追加することはできます。素のオブジェクトであるPSObjectをNew-Objectして作ったオブジェクトでもこれは可能なので、一応ユーザー定義オブジェクトを作ることは可能です。Add-Memberコマンドレットを使うのはちょっとめんどくさいです。PowerShellインアクション」ではAdd-Memberを使いPowerShellの関数を駆使してクラス定義構文のようなものを実装した例はありますが、いささか大仰な感は否めません。連想配列をユーザー定義オブジェクト代わりに使うと、簡単にできますしそこそこ便利に使えます。連想配列をオブジェクトの代わりにすることのメリットとデメリット連想配列をオブジェクトの代わりにすることのメリットは以下の三点があるかと思います。簡単な記述(連想配列のリテラル)でオブジェクトが作成できるPowerShellの連想配列リテラル@{}を使うことで、簡単に記述できます。それを配列化するのも@()を使うと容易です。})ドット演算子で値の参照、設定ができるPowerShellの連想配列は「連想配列[キー名]」のほかに、「連想配列.キー名」でもアクセスできる。連想配列の配列に対してWhere-Objectコマンドレットでフィルタをかけることができるこれは2とも関係しているのですが、通常のオブジェクトと同様にWhere-Objectコマンドレットでのフィルタ、ForEach-Objectコマンドレットでの列挙が可能です。$_.name}このようにメリットはあるのですが、本物のオブジェクトではないのでそれに起因するデメリットがいくつかあります。要素(プロパティ)をいくらでも自由に追加できてしまうこれはメリットではあるのですが、デメリットでもある点です。後述するデメリットのせいで、同じキーをもつ連想配列の配列を作ったつもりでも、どれかのキー(プロパティ名)を間違えていた場合、それを検出するのが困難です。メソッドがうまく記述できない連想配列要素にスクリプトブロックを指定し、&演算子で実行することでメソッド的なことはできます。このスクリプトブロック内では$thisが使えず、オブジェクトのプロパティにアクセスすることができないのでいまいちです。$pcItem.price}ならOKだが...Get-Member、Format-List、Format-Tableなどが使えないこれらのコマンドレットはあくまで連想配列オブジェクト(Hashtable)に対して行われるので、意図した結果になりません。8000こんな表示になってしまいます。連想配列をユーザー定義オブジェクトに変換する関数ConvertTo-PSObjectこのように、連想配列の記述のお手軽さは捨てがたいものの、いくつかの問題点もあるのが現実です。そこで連想配列のお手軽さを生かしつつ、ユーザー定義オブジェクトの利便性も取るにはどうすればいいか考えました。結論は、「連想配列を変換してユーザー定義オブジェクトにする関数を書く」というものでした。それが以下になります。}}ご覧のようにコード的には割にシンプルなものが出来ました。連想配列またはその配列をパラメータにとり、またはパイプラインから渡し、連想配列要素をプロパティまたはメソッドに変換してPSObjectにAdd-Memberしてるだけです。-recurseパラメータを付けると連想配列内に連想配列がある場合に再帰的にすべてPSObjectに変換します。それでは実際の使用例を挙げます。パラメータに連想配列を渡すとPSObjectに変換する。連想配列をコードで組み立てていく例。「私の名前はmutaguchiです。」と表示$muta.speak("こんにちは。※配列要素に連想配列以外の値が含まれている場合は展開しないこのように簡単な関数一つで、連想配列にあった問題点をすべて解消しつつ簡単な記述で独自のオブジェクトを記述できるようになりました。便利だと思いますのでぜひ使ってみてください。余談ですが、今回使用したNotePropertyはプロパティに代入できる型を指定したり、リードオンリーなプロパティを作ったりすることができません。いうのを作りたい場合はScriptPropertyを使います。Add-Memberコマンドレットの-valueパラメータにゲッターを、-secondValueパラメータにセッターをそれぞれスクリプトブロックで記述します。こいつはあまりいけてないです。これらのスクリプトブロック内で参照するフィールドを別途Add-MemberでNotePropertyを使って作成する必要があるのですが、これをprivateにすることができません。よってGet-Memberでもばっちり表示されてしまいますし、フィールドを直接書き換えたりもできてしまいます。今回のように連想配列をPSObjectに変換する場合はprivateフィールド名も自動生成する必要があるのですが、それをScriptProperty内のゲッター、セッターから取得する方法がなく、たぶんInvoke-Expressionを使うしかありません。これらを踏まえて元の連想配列要素の値の型を引き継ぎ、それ以外の型を代入できないようにしたScriptPropertyバージョンも一応書いてみました。ConvertTo-PSObject関数のelse句の部分を以下に置き換えます。"]`$args[0]}"|iexまあこれはいまいちなんで参考程度に。2012/08/23追記この記事を書いた時は知らなかったのですが、実は単にNotePropertyだけを持つユーザー定義オブジェクトを作成するのであれば、もっと簡単な方法があります。3.0$o=[pscustomobject]@{Code=137;Name="23インチ液晶ディスプレイ"}3つのコードはほぼ等価です。2.0と3.0では連想配列リテラルを用いて簡単にカスタムオブジェクトを作れるようになりました。@mutaguchiBooks書籍版。