Help us understand the problem. What is going on with this article?

[Powershell]自作オブジェクト作る練習

More than 1 year has passed since last update.

概要

とりあえず基礎っぽいものってことで、以下の構成のオブジェクトを作成してみた。
実際は、Get-Hogeでなんかの情報取得して、Where-ObjectやSelect-Object使って特定のプロパティ抜き出したものでオブジェクト作ることが多いと思う。
CSV読み込んでデータ処理したい場合なんかにもたぶん便利。

↓それっぽいクラス図

TestObject
  + Index[]:int
  + Name[]:string  #最初に適当に定義しておく
  + Width[]:int  #値は適当に乱数で決める
  + Height[]:int  #値は適当に乱数で決める
  + TriangleArea[]:double  #上記2つから求めた三角形の面積
  + SearchForIndex():object  #Index番号を指定して検索するメソッド
  + Search():object  #検索に使うプロパティ名と値を指定して検索するメソッド

先に最終的なコードを掲載

function Get-TestObject{
    $obj_ary = @()    # return用オブジェクト

    $names = @(
        "foo"
        "bar"
        "hoge"
        "fuga"
        "fuga"    # 同名でも可
    )

    # 各プロパティを定義
    $i = 1
    foreach($item in $names){
        $obj = New-Object PSCustomObject
        $obj | Add-Member -NotePropertyMembers @{
            Index = $i
            Name = $item
            Height = ( 1 + (Get-Random 9) )    # ランダムで110代入
            Width = ( 1 + (Get-Random 9) )    # ランダムで110代入
        }
        $obj | Add-Member -MemberType ScriptProperty -Name TriangleArea -Value {
            $this.Width * $this.Height / 2
        }

        $i += 1
        $obj_ary += ($obj | Select-Object Index, Name, Width, Height, TriangleArea)
    }

    # Index番号を指定して検索するメソッド
    $obj_ary | Add-Member -MemberType ScriptMethod -Name SearchForIndex -Value {
        param($index)
        $ret = ($this | Where-Object { $_.Index -eq $index })
        return $ret
    }

    # 検索に使うプロパティ名と値を指定して検索するメソッド
    $obj_ary | Add-Member -MemberType ScriptMethod -Name Search -Value {
        param($prop, $value)
        $ret = ($this | Where-Object { $_.$prop -eq $value })
        return $ret
    }

    # return
    $obj_ary
}
# テスト用実行コマンド
$test = Get-TestObject
echo '##### $test ###############'
echo $test
echo '##### $test | ft ###############'
echo $test | ft
echo '##### $test | Get-Member ###############'
echo $test | Get-Member
echo '##### ($test).name ###############'
echo ($test).name
echo '##### ($test).SearchForIndex(1) | ft ###############'
echo ($test).SearchForIndex(1) | ft
echo '##### ($test).Search("Name", "fuga") | ft ###############'
echo ($test).Search("Name", "fuga") | ft
echo '##### $test | Where-Object { $_.TriangleArea -gt 10 } | ft ###############'
echo $test | Where-Object { $_.TriangleArea -gt 10 } | ft
echo '####################'

申し訳程度の解説

だいたいコード見ればわかるけど、ポイントはたぶん3つ。

  1. PSCustomObjectで1レコード分のオブジェクトを作る
    • 単純に連想配列で作ると綺麗にならない
  2. オブジェクトにパイプでAdd-Memberに渡してプロパティやメソッドを追加する
  3. 追加するときにSelect-Objectを指定すると表示される順番を決められる

特にAdd-Memberはオプションが多くてややこしい。
あんまりわかってないけど、とりあえず以下の2つだけわかってれば使えると思う。

  • 静的なプロパティを定義したい場合
Add-Member -NotePropertyMembers 配列
#※Powershell 3.0以上なら「[PSCustomObject] 配列」でも可
#※プロパティが一つだけなら、以下と同じく-Nameと-Valueで指定しても可
  • 静的なプロパティを使って加工したプロパティを定義したい場合
Add-Member -MemberType ScriptProperty -Name プロパティ名 -Value {スクリプト}
#※{ }の中で、$thisを使って静的プロパティの値を参照できる

実行結果の例

テスト用のコードの結果は以下のようになる。
オブジェクトになっているから、特定のプロパティ名だけ抜き出したり、Format-Tableに渡して表示したりってことが通常のオブジェクトを扱うのと同じようにできる。
CSVやHTMLへのエクスポートもできる。

##### $test ###############


Index        : 1
Name         : foo
Width        : 1
Height       : 6
TriangleArea : 3

Index        : 2
Name         : bar
Width        : 5
Height       : 9
TriangleArea : 22.5

Index        : 3
Name         : hoge
Width        : 1
Height       : 6
TriangleArea : 3

Index        : 4
Name         : fuga
Width        : 4
Height       : 9
TriangleArea : 18

Index        : 5
Name         : fuga
Width        : 4
Height       : 7
TriangleArea : 14

##### $test | ft ###############



Index Name Width Height TriangleArea
----- ---- ----- ------ ------------
    1 foo      1      6            3
    2 bar      5      9         22.5
    3 hoge     1      6            3
    4 fuga     4      9           18
    5 fuga     4      7           14


##### $test | Get-Member ###############


   TypeName: Selected.System.Management.Automation.PSCustomObject

Name           MemberType   Definition                     
----           ----------   ----------                     
Equals         Method       bool Equals(System.Object obj) 
GetHashCode    Method       int GetHashCode()              
GetType        Method       type GetType()                 
ToString       Method       string ToString()              
Height         NoteProperty int Height=6                   
Index          NoteProperty int Index=1                    
Name           NoteProperty string Name=foo                
TriangleArea   NoteProperty System.Int32 TriangleArea=3    
Width          NoteProperty int Width=1                    
Search         ScriptMethod System.Object Search();        
SearchForIndex ScriptMethod System.Object SearchForIndex();
##### ($test).name ###############
foo
bar
hoge
fuga
fuga
##### ($test).SearchForIndex(1) | ft ###############



Index Name Width Height TriangleArea
----- ---- ----- ------ ------------
    1 foo      1      6            3


##### ($test).Search("Name", "fuga") | ft ###############

Index Name Width Height TriangleArea
----- ---- ----- ------ ------------
    4 fuga     4      9           18
    5 fuga     4      7           14


##### $test | Where-Object { $_.TriangleArea -gt 10 } | ft ###############

Index Name Width Height TriangleArea
----- ---- ----- ------ ------------
    2 bar      5      9         22.5
    4 fuga     4      9           18
    5 fuga     4      7           14


####################

おまけ(特殊フォルダの一覧を取得)

デスクトップとかマイドキュメントとか、環境変数でパスが定義されている特殊なフォルダのパスをすべて取得する。
特殊フォルダすべての名前とパスは元々同じオブジェクトに入っていないため、上手く文字列を結合して表示しないと見づらいものになってしまう。
こんなときオブジェクトを使うと、コードも表示結果も見やすくて扱いやすいスマートな書き方ができる。

function Get-SpecialFolders{
    $obj_ary = @()
    $specialfolders = ([environment+specialfolder] | Get-Member -MemberType Properties -static)

    foreach($item in $specialfolders){
        $obj = New-Object PSCustomObject
        $obj | Add-Member -NotePropertyMembers @{
            Name = ($item.Name)
            Path = ([environment]::GetFolderPath($item.Name))
        }

        $obj_ary += ($obj | Select-Object Name, Path)
    }

    $obj_ary
}

結果はこんな感じ。

> Get-SpecialFolders

Name                   Path                                                                                     
----                   ----                                                                                     
AdminTools             C:\Users\Owner\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools
ApplicationData        C:\Users\Owner\AppData\Roaming                                                           
CDBurning              C:\Users\Owner\AppData\Local\Microsoft\Windows\Burn\Burn                                 
CommonAdminTools       C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools  

また、例えばDドライブのフォルダだけを取得したい場合はPowershellの基本通り、

Get-SpecialFolders | Where-Object { $_.Path -match "^D" }

と書けばよい。
一度必要な情報だけでオブジェクトを作ってしまえば、後は余計な変数やプロパティを気にせずに扱えてとても便利。


参考ページ

カスタムオブジェクトの使い方などの解説
powershell チートシート
PowerShellのカスタムオブジェクト(PSCustomObject)の使い方
PowerShell再入門:11. PSObjectとは

Add-Memberの詳細な解説
Add-Member を極める

特殊フォルダの取得コード
PowerShell: ◆特殊フォルダーの取得

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした