Export-CSV や Format-Table で出力した結果に、一部プロパティが出たり出なかったりするという処理がありました。
コードを見てみると Add-Member が使われていたのですが、これが条件によっては一部メンバーが追加されたりされなかったりすることが分かりました。
最初のループで一部のメンバーが追加されていないと、それ以降の処理は追加したプロパティが表示されません。
酷いときには
"Length"
"5"
"7"
以下数字が続く…
なんてことも。
原因はプロパティの指定にありました。
出力の列名の既定値は最初のオブジェクトが使われる
例えば Format-Table の列名は -Property で指定します。
今回はこれが省略されていました。
このパラメーターを省略した場合、表示に表示されるプロパティは、最初のオブジェクトのプロパティによって異なります。 たとえば、最初のオブジェクトに PropertyA および PropertyB が含まれているのに、後続のオブジェクトに PropertyA、 PropertyB、および PropertyC がある場合、 PropertyA および PropertyB ヘッダーのみが表示されます。
Export-CSV の方はちょっとわかりにくいところに書かれていますが、こちらも同様です。
NoTypeInformation を使用する場合、最初の文字列には列ヘッダーが含まれます。 ヘッダーには、最初のオブジェクトのプロパティ名が文字区切りリストとして含まれています。
Export-Csvには -Property は無いので、 Select-Object を前に入れるなどひと工夫必要そうです。
$result | Select-Object Hoge,Fuga | Export-CSV -Path $OutPath
実際にやってみた
1列しかないオブジェクトと2列あるオブジェクトを、順番を変えて追加して出力してみます。
$a = [PSCustomObject]@{p1=1}
$b = [PSCustomObject]@{p1=1;p2=2}
# 1列→2列の順番
$list = @()
$list += $a
$list += $b
$list | Format-Table
# 2列→1列の順番
$list = @()
$list += $b
$list += $a
$list | Format-Table
結果はこちら。最初に列が少ないほうのオブジェクトが格納されていると、プロパティ p2 の値が結果に出てこないことがわかります。
確実にプロパティを出したい時はどうするか?
一つのコマンドで複数のオブジェクトが返ってくることはよくあります。
身近なものではGet-ChildItem でしょうか。フォルダとファイルを同時に返すことがあり、DirectoryInfo と FileInfo でプロパティ構造が異なります。
この例では順番をファイルを先にするかフォルダを先にするかで明らかに結果が違うことがわかります。
素直に -property を使う
プロパティが既知ならこれがベスト。
プロパティの無いオブジェクトがあっても特に怒られないのがうれしい。
メンバー一覧を調べて重複排除
具体的なプロパティがわからないようであれば、以下のようにしてみてはどうでしょう。
- 全部のオブジェクトのプロパティ名一覧を集める
- Select-Object -Unique を使って重複排除
- 必要な条件(例えば正規表現や連番)のものを抽出して Select-Object する
Set-StrictMode -Version latest
$a = [PSCustomObject]@{p1="a1"}
$b = [PSCustomObject]@{p1="b1";p2="b2"}
$c = [PSCustomObject]@{p1="c1";p2="c2";"不要なプロパティ"=999}
# 少ない順に入れてみる
$list = @()
$list += $a
$list += $b
$list += $c
# プロパティ一覧を作る
$Props = $List | %{$_.PSObject.Properties.Name} | Select-Object -Unique
# 参考:PSObject以外で汎用的に作るならこちらのコードを。
# $Props = $list | Foreach-Object {$_ | Get-Member -MemberType NoteProperty} | Select-Object -Expand Name -Unique
# Property を指定することで表示できる
$list | Format-Table -Property $Props
# 「p1~p100 という名前のプロパティだけ欲しい」なんてこともできる
$p = @{}
$probProp = 1..100 | %{"p$_"}
$props = $props | Where-Object {$probProp -contains $_}
$list | Format-Table -Property $Props
参考になれば幸いです。