このページの本文へ

Windows Info 第448回

PowerShellで面倒なオブジェクトはPSCustomObjectに変換するのが早道

2024年09月08日 10時00分更新

文● 塩田紳二 編集● ASCII

  • この記事をはてなブックマークに追加
  • 本文印刷

PSCustomObjectとはそもそもなんぞや

 PowerShellのパイプラインは、オブジェクトを流すようになっている。なので、複雑な情報はPowerShellのオブジェクトにすると、あとの処理が簡単になる。

 そのためにあるのが、「PSCustomObject」と呼ばれる汎用のオブジェクトだ。このオブジェクトであれば、Format-*や*-ObjectといったPowerShellの汎用コマンドを適用できる。

 逆に言えば、PowerShellの汎用コマンドは、フラットな構造のオブジェクトを想定しており、プロパティの値がオブジェクトになっているようなものは扱いにくい。このような場合に、PSCustomObjectを作ってフラットな構造にすることで、以後は処理しやすくなる。

 なお、PSCustomObjectの基本的なことは、Microsoftのサイトにページ(https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_pscustomobject?view=powershell-7.4)がある。

PSCustomObjectの作り方

 PSCustomObjectを作るには、ハッシュテーブル(連想配列)を作り、それをPSCustomObjectにキャストすればいい。まず、ハッシュテーブルは、「名前=値;」を「@{」と「}」で囲む。たとえば、

@{Name="shioda"; Country="Japan"; Job="Writor" }

とする。

PSCustomObject

PSCustomObjectを作るには、まずハッシュテーブルを作る。ハッシュテーブルは「名前=値」をセミコロンで区切って、「@{」と「}」で囲む。これをPSCustomObjectでキャスト(角カッコで囲んでハッシュテーブルの前につける)すれば、PowerShell用の汎用オブジェクトになる

 PSCustomObjectにするには、前に「[PSCustomObject]」を置く(キャストという)。

[PSCustomObject]@{Name="shioda"; Country="Japan"; Job="Writor" }

 これが基本パターンだが、多くの場合、パイプラインを流れてくるオブジェクトを利用してPSCustomObjectを作る。その基本パターンは、

<ソースコマンド> | Foreach-Object {[PSCustomObject] @{<名前>=<値>;…… }

となる。たとえば、PowerShellでは、「1..10」で1から10までの整数オブジェクトを発生させることができる。これをパイプラインでForeach-Objectで処理する。

0x41..0x45 | ForEach-Object { [PSCustomObject]@{ Code=$_; Hex=$_.ToString("X2"); Char=[char]$_ }}

PSCustomObject

2つの数値を「..」でつなげると、その間の整数値を生成することができる。これを「ソース」として、パイプラインでForeach-ObjectでPSCustomObjectを作成する。PowerShellでの基本パターンである

 PSCustomObjectになったらSort-ObjectやWhere-Object、Select-Objectで処理が可能だ。

もう少し複雑な例を挙げてみよう

 PowerShellでは、XMLデータを扱えるが、多くの場合でXMLデータは階層構造を持ち、PowerShellからはデータのアクセスが面倒だ。このようなときに、PSCustomObjectに必要な情報だけを入れることで、フラットなオブジェクトとして扱うことができる。

 PowerShellでは、XMLオブジェクトを作り、これを使ってXMLファイルを読み込む。具体的な手順としては、

$myxml=New-Object System.Xml.XmlDocument
$myxml.Load(<XMLフルファイルパス>)

となる。Loadメソッドにはフルパスが必要な点に注意してほしい。

PSCustomObject

PowerShellでは、XMLファイルを解釈して扱うことができる。このとき、Select-Xmlコマンドを使う面倒だが正式な方法と、簡易だが大文字小文字を区別できない方法の2つがある

 XMLファイルがXMLオブジェクトに読み込まれたら、アクセス方法は2つ。正統な方法は、Select-XMLコマンドを使って、XPATHで要素にアクセスする方法。もう1つは、PowerShellのオブジェクトのようにドット記法を使って要素にアクセスする方法だ。

 ただし、後者はプロパティ名の大文字小文字の区別がないため、大文字小文字だけが異なる要素を区別できない。簡易的には、後者の方法でも問題ないことが多いのだが、厳密には、XMLではタグ名や属性名では大文字小文字が区別されているため、XMLファイルによっては、対象を混同してしまうことがある。

 ここでは、OpenTypeフォント(Cascadia Codeフォント)のソースコードから、グリフ名やユニコードのコードポイントなどを取得してみる。Select-Xmlコマンドを使うと、

(Select-Xml $myxml -XPath "//unicode/@hex").node

となる(大文字小文字が区別されるのでXPATHの記述に注意)。大文字小文字を区別できない簡易な方法では、

$myxml.glyph.unicode.hex

だけでよい。これでもプロパティ指定が煩雑で、一覧表にするのが面倒になる。

 そこで、以下のコマンドを使ってPSCustomObjectに変換する。

Get-ChildItem -Recurse -Filter *.glif | ForEach-Object { $myxml.Load($_.FullName); [PSCustomObject]@{Name=$myxml.glyph.name; Unicode=$myxml.glyph.unicode.hex; Width=$myxml.glyph.advance.width; File=$_.Name} }

PSCustomObject

XMLオブジェクトをPSCustomObjectオブジェクトにすることで、Select-Objectのような汎用コマンドが利用可能になる。このほかにもWhere-ObjectやSort-Objectが使える

 少し長いので、改行とインデントを入れてわかりやすくしたのが、以下のリストである。

Get-ChildItem -Recurse -Filter *.glif |
    ForEach-Object {
        $myxml.Load($_.FullName);
        [PSCustomObject]@{
            Name=$myxml.glyph.name;
            Unicode=$myxml.glyph.unicode.hex;
            Width=$myxml.glyph.advance.width;
            File=$_.Name
        }
    }

 対象のフォルダには、大量のglifファイルがあるので、出力も大量になるが、Where-ObjectやSelect-Objectコマンドが使えるためコンパクトな出力が可能になる。また、Sort-ObjectでUnicodeプロパティをキーに並べ替えることも可能だ。Sort-ObjectやSelect-Objectは、ドット記法を解釈しないため、XMLオブジェクトを直接扱えない。

 このような場合、PSCustomObjectを使い、一旦PowerShell用のフラットなオブジェクトを作る。あとの作業は、Select-ObjectやWhere-Objectなど、ハイフンの後ろに「Object」を持つコマンドや、コマンド名のハイフンよりも前に「Format」を持つコマンドが利用できるようになる。

 PowerShellは、パイプラインでオブジェクトを扱えるのが「自慢」だが、フラットでない構造のオブジェクト(プロパティにオブジェクトが入っているなど)はあまり得意ではない。オブジェクトへのアクセスが面倒な場合、PSCustomObjectを作るのが早道である。

カテゴリートップへ

この連載の記事

注目ニュース

ASCII倶楽部

プレミアムPC試用レポート

ピックアップ

ASCII.jp RSS2.0 配信中

ASCII.jpメール デジタルMac/iPodマガジン