PowerShellで「1件分のデータ(レコード)」を表すのに使うのがPSCustomObjectです。名前・年齢・部署のように、複数の項目をひとまとめにして持ち、一覧表示やCSV出力にきれいに対応できます。ログの集計結果や、処理対象のリストを作るときに欠かせません。
似たものにハッシュテーブルがありますが、用途が違います。特に重要なのが、ハッシュテーブルはCSVに出力するとデータではなく内部情報が出てしまうのに対し、PSCustomObjectはきれいな表として出力できる点です。この記事では、実機で確認しながら、PSCustomObjectの作り方と使いどころを整理します。
- 作り方は
[PSCustomObject]@{ Name = "山田"; Age = 30 }です。書いた順番(プロパティの順序)が保たれます。 - 値の参照は
$obj.Nameのようにプロパティでアクセスします。 - ハッシュテーブルは「キーで値を引く」用途、
PSCustomObjectは「1件のレコードを表す」用途です。 - CSV出力やテーブル表示には
PSCustomObjectを使います。ハッシュテーブルをExport-Csvすると内部情報が出てしまいます。 - プロパティの追加は
Add-Member、ループで[PSCustomObject]@{}を集めれば一覧データになります。 - 一覧表示は
Format-Table、CSV出力はExport-Csv -NoTypeInformationです。
ハッシュテーブルとの基本的な違いは配列とハッシュテーブルの使い方、JSONとの相互変換はJSONを読み書きする方法、一覧の絞り込みや計算プロパティはWhere-Object・Select-Object・Sort-Objectもあわせて参考になります。
PSCustomObjectを作る
作り方はとても簡単で、ハッシュテーブルの前に[PSCustomObject]を付けるだけです。プロパティ名と値のペアを書きます。
# [PSCustomObject] を付けて作る
$user = [PSCustomObject]@{
Id = 1
Name = "山田"
Age = 30
}
# プロパティでアクセスする
$user.Name # 山田
$user.Age # 30
# 型を確認すると PSCustomObject
$user.GetType().Name # PSCustomObject
実機でも、$user.Nameで山田が取れ、型はPSCustomObjectになりました。プロパティの順番(Id → Name → Age)は、書いた順のまま保たれます。これが、次に説明するハッシュテーブルとの大きな違いです。
【重要】ハッシュテーブルとの違い
[PSCustomObject]@{}と@{}(ハッシュテーブル)は見た目が似ていますが、性質と用途が違います。実機で確認した結果をまとめます。
| 項目 | ハッシュテーブル @{} |
PSCustomObject |
|---|---|---|
| 主な用途 | キーで値を引く(辞書) | 1件のレコードを表す |
| アクセス | $ht["Name"] / $ht.Name |
$obj.Name |
| 順序 | 保証されない | 書いた順を保つ |
| 一覧表示 | 崩れる | きれいな表になる |
| CSV出力 | 内部情報が出る(使えない) | 列としてきれいに出る |
# ハッシュテーブルは順序が保証されない
$ht = @{ Id = 1; Name = "山田"; Age = 30 }
$ht.Keys # 実機では Age, Name, Id の順(書いた順ではない)
# PSCustomObject は書いた順を保つ
$obj = [PSCustomObject]@{ Id = 1; Name = "山田"; Age = 30 }
$obj.PSObject.Properties.Name # Id, Name, Age(書いた順)
実機でも、同じ内容でもハッシュテーブルのキー順はAge, Name, Idと入れ替わり、PSCustomObjectはId, Name, Ageと書いた順を保ちました。一覧やCSVでは列の順番が大事なので、表示・出力用のデータはPSCustomObjectで作ります。
プロパティを追加・変更する
作ったあとに値を変えるのは、プロパティへの代入でできます。新しいプロパティを足すにはAdd-Memberを使います。
$user = [PSCustomObject]@{ Id = 1; Name = "山田" }
# 既存プロパティの変更
$user.Name = "佐藤"
# プロパティの追加
$user | Add-Member -NotePropertyName "City" -NotePropertyValue "東京"
$user.City # 東京
# まとめて複数追加するなら、最初から全部書くほうが簡単
$user2 = [PSCustomObject]@{
Id = 2; Name = "鈴木"; City = "大阪"; Age = 28
}
実機でも、Add-MemberでCityを追加すると、プロパティがId, Name, Cityと末尾に足されました。後から1つ2つ足すならAdd-Member、最初から項目が決まっているなら、宣言時にまとめて書くほうがすっきりします。
オブジェクトの配列を作る(ループで集める)
PSCustomObjectの真価は、同じ形のオブジェクトを並べて一覧データにするときに発揮されます。ループの中で[PSCustomObject]@{}を出力し、全体を変数で受け取ります。
# ループで同じ形のオブジェクトを集める
$results = foreach ($i in 1..3) {
[PSCustomObject]@{
No = $i
Square = $i * $i
}
}
$results.Count # 3
$results[1].Square # 4
# 実例: ファイル一覧を整形して持つ
$files = Get-ChildItem "C:\work" -File | ForEach-Object {
[PSCustomObject]@{
名前 = $_.Name
サイズKB = [math]::Round($_.Length / 1KB, 1)
更新日 = $_.LastWriteTime.ToString("yyyy/MM/dd")
}
}
実機でも、ループで作った$resultsは3件のオブジェクト配列になり、$results[1].Squareで4が取れました。Get-ChildItemの結果を必要な項目だけに整形して持つ、といった使い方が実務では頻出します。ループの結果を変数で受ける書き方はループ完全ガイドでも解説しています。
一覧で表示する(Format-Table / Format-List)
オブジェクトの配列は、そのまま表示すると自動で表(テーブル)になります。明示的に整形するならFormat-Table(表)やFormat-List(縦並び)を使います。
# 表形式で表示(プロパティが少ないと自動でこの形) $results | Format-Table # 縦並びで表示(プロパティが多いときや詳細確認に) $results | Format-List # 表示する列を選ぶ $results | Format-Table No, Square -AutoSize
プロパティが4つ以下なら自動で表に、5つ以上だと自動で縦並び(リスト)になります。-AutoSizeを付けると、列幅が内容に合わせて調整されます。なお、Format-Tableなどの整形コマンドは表示専用です。整形した後のデータを変数に入れて再利用したり、CSVに出したりはできないため、整形は一番最後に使います。
CSVに出力する(Export-Csv)
オブジェクトの配列はExport-CsvでそのままCSVファイルにできます。プロパティ名がそのまま列名になります。ここで、ハッシュテーブルとの決定的な違いが出ます。
# PSCustomObject はきれいなCSVになる
$results | Export-Csv "C:\work\out.csv" -NoTypeInformation -Encoding UTF8
# ヘッダー: "No","Square" ← プロパティ名が列になる
# NG: ハッシュテーブルを Export-Csv すると内部情報が出る
@{ Id = 1; Name = "山田" } | Export-Csv "C:\work\ng.csv" -NoTypeInformation
# ヘッダー: "IsReadOnly","IsFixedSize","Keys","Values","Count" ...
# ← データではなくハッシュテーブルの内部プロパティが出てしまう
実機で確認したところ、オブジェクト配列のExport-Csvはヘッダーが"No","Square"ときれいな列になりました。一方、ハッシュテーブルをExport-Csvすると、ヘッダーが"IsReadOnly","IsFixedSize","Keys","Values","Count"などになり、データではなくハッシュテーブルの内部情報が出力されてしまいました。CSVやテーブル表示に使うデータは、必ずPSCustomObjectで作ってください。-NoTypeInformationは先頭の型情報行を消すための指定です(PowerShell 6以降は既定で付きません)。CSVの文字コードは-Encoding UTF8で指定します。
Select-Objectや計算プロパティと組み合わせる
すでにあるオブジェクト(コマンドの出力など)から必要な項目を選んでPSCustomObject相当にするなら、Select-Objectが便利です。計算した新しい列も作れます。
# Get-Process から必要な項目だけ選ぶ(結果は表示・CSV向きのオブジェクト)
Get-Process |
Select-Object Name, Id, @{
Name = "メモリMB"
Expression = { [math]::Round($_.WorkingSet / 1MB, 1) }
}
# そのまま CSV に出せる
Get-Process |
Select-Object Name, Id |
Export-Csv "C:\work\process.csv" -NoTypeInformation -Encoding UTF8
Select-Objectでプロパティを選んだり計算プロパティを足したりした結果も、表示やCSV出力に使えるオブジェクトになります。Select-Objectや計算プロパティの詳細はWhere-Object・Select-Object・Sort-Objectを参照してください。手作りの[PSCustomObject]@{}と、Select-Objectでの加工を、場面で使い分けます。
よくある失敗
CSV出力にハッシュテーブルを使う
ハッシュテーブルをExport-Csvすると内部情報が出ます。CSVやテーブル表示にはPSCustomObjectを使います。
プロパティの順序が崩れる
順序が大事なら[PSCustomObject]@{}を使います。ハッシュテーブルはキーの順序を保証しません。
Format-Tableした結果を再利用しようとする
Format-Tableなどは表示専用です。整形後のデータは加工やCSV出力に使えません。整形は最後に行い、それまではオブジェクトのまま扱います。
ループ内で配列に+=して集める
オブジェクトを集めるなら、$list = foreach (...) { [PSCustomObject]@{} }のようにループ全体を変数で受けるほうが、+=より高速です。
古いNew-Objectの書き方で順序が崩れる
New-Object PSObject -Property @{...}はハッシュテーブルを元にするため順序が保証されません。今は[PSCustomObject]@{}を使えば順序が保たれます。
よくある質問
PSCustomObjectです。特にCSVやテーブル表示にはPSCustomObjectを使ってください。[PSCustomObject]@{}を使えば、書いた順番がそのまま保たれます。通常のハッシュテーブルや古いNew-Object PSObjectは順序を保証しないため、列の順番が大事なときは[PSCustomObject]を使います。$obj | Add-Member -NotePropertyName "列名" -NotePropertyValue 値で追加できます。最初から項目が決まっているなら、宣言時にまとめて書くほうが簡単です。Select-Objectで必要なプロパティを選び、Export-Csv -NoTypeInformation -Encoding UTF8で出力します。選んだプロパティ名がそのまま列名になります。$list = foreach (...) { [PSCustomObject]@{} }のように、ループ全体を変数で受けると、出力したオブジェクトが配列として集まります。+=で足すより高速で読みやすくなります。まとめ
PSCustomObjectは[PSCustomObject]@{ ... }で作り、書いた順のプロパティ順序を保ちます。- ハッシュテーブルは「キーで引く」用途、
PSCustomObjectは「1件のレコード」用途です。 - CSV出力やテーブル表示には
PSCustomObjectを使います。ハッシュテーブルは内部情報が出てしまいます。 - プロパティ追加は
Add-Member、一覧はループで[PSCustomObject]@{}を集めます。 - 表示は
Format-Table/Format-List(表示専用なので最後に使う)。 - CSVは
Export-Csv -NoTypeInformation -Encoding UTF8で出力します。
PowerShellでデータを「見せる・出力する」段になったら、PSCustomObjectの出番です。ハッシュテーブルとの使い分け、とりわけCSV出力での違いさえ押さえれば、集計結果やファイル一覧を、きれいな表やCSVとして自在に出力できるようになります。これでPowerShellの基礎は一通りそろいました。
