みなさんこんにちは!ヒロポンです!
PowerShell でちょっとした自動化を書いてると、「設定をまとめて持ち回りたい」「関数に渡す引数が多すぎてつらい」って場面、来ますよね??
そこで顔を出すのが ハッシュテーブル @{} と PSCustomObject、それに スプラッティング。全部 @{...} っぽい見た目で、どれをどう使うのか正直こんがらがる。
俺も最初、全部ハッシュテーブルで押し切ろうとして、CSV 出力でぐちゃぐちゃにした。
今回は PowerShell のハッシュテーブル と PSCustomObject、それにスプラッティングがそれぞれ何のための入れ物なのかを、1人で運用を回す自動化の目線で順番に積んでいきます。役割の違いが腑に落ちると、取り回しが一気に楽になる。
忙しい人向けに最初にまとめ
- ハッシュテーブル
@{}… キーと値をひくための入れ物。ただし順序は保証されない([ordered]@{}で順序付き) - PSCustomObject … プロパティを持つオブジェクト。記述順を保持し、
Export-Csvや表出力に向く - スプラッティング … コマンドの引数をハッシュテーブルにまとめて、
$ではなく@を付けて渡す書き方 - 一番ハマるのは「ハッシュテーブルをそのまま
Export-Csvに流して崩れる」やつ。表・CSV にするなら PSCustomObject
以下、それぞれ実際に動かしながら見ていきます。
そもそも、この3つは何のための入れ物か
まず全体像。似て見えるけど、狙ってる用途がまるで違います。
- ハッシュテーブルは「
$設定['ServerName']みたいに、キーで値をひきたい」時の入れ物 - PSCustomObject は「1件のデータを、名前付きのプロパティを持つオブジェクトとして扱いたい」時の入れ物
- スプラッティングは入れ物というより「引数の渡し方」で、中身はハッシュテーブル
この住み分けを外すと、「ハッシュテーブルで CSV を出そうとして崩れる」みたいな事故になる。逆に頭に入ってれば、設定はハッシュテーブル、出力は PSCustomObject、コマンド呼び出しはスプラッティング、と迷わず手が動きます。
ここまでで分かったこと: 3つは用途が別。「ひく」ハッシュテーブル、「出す」PSCustomObject、「渡す」スプラッティング。
ハッシュテーブル @{} — キーと値の入れ物
一番基本の入れ物です。@{ キー = 値 } で作って、キーで値をひく。
$config = @{
ServerName = "SV-BATCH01"
Port = 1433
Retry = 3
}
# キーで値をひく (両方同じ)
$config["ServerName"]
$config.ServerName
# 後から足す
$config["Timeout"] = 30
こんな感じで、設定値やパラメータを名前でまとめて持てる。ちょうどいい入れ物です。
で、ここで業務でよくハマる1つ目。ハッシュテーブルは キーの順序が保証されません。書いた順に並ぶとは限らない。
$config = @{ A = 1; B = 2; C = 3 }
$config # A, B, C の順で出るとは限らない
「設定を書いた順に表示したい」「順番が意味を持つ」時は、[ordered] を付けます。
$config = [ordered]@{ A = 1; B = 2; C = 3 }
$config # A, B, C の順が保たれる
[ordered]@{} は中身が OrderedDictionary になって、記述順が保たれる。地味だけど、出力の見た目が絡む時にじわっと効いてきます。
ここまでで分かったこと: ハッシュテーブルはキーで値をひく入れ物。既定は順序保証なし、順番が要るなら
[ordered]。
PSCustomObject — プロパティを持つオブジェクト
次。PSCustomObject は、ハッシュテーブルにそっくりな書き方なのに、できあがるものが別物です。
$row = [PSCustomObject]@{
Name = "田中"
Dept = "経理"
Active = $true
}
$row.Name # プロパティとしてアクセス
$row # Name / Dept / Active を持つ「オブジェクト」として表示される
見た目は [PSCustomObject] を頭に足しただけ。でもこれは「キーと値の入れ物」じゃなく、Name・Dept・Active というプロパティを持つ1件のオブジェクトになる。
しかも PSCustomObject は、記述した順にプロパティが並ぶ。ハッシュテーブルと違って順序を気にしなくていい。
何がうれしいか。パイプラインに流した時です。オブジェクトだから、Format-Table でも Export-Csv でも、プロパティがそのまま列になる!!
$people = @(
[PSCustomObject]@{ Name = "田中"; Dept = "経理" }
[PSCustomObject]@{ Name = "佐藤"; Dept = "総務" }
)
$people | Format-Table
$people | Export-Csv -Path "people.csv" -NoTypeInformation -Encoding UTF8
こんな感じで、1件=1オブジェクトを並べて表や CSV にできる。1人運用の自動化だと、AD ユーザーの棚卸し結果とかログの集計結果を CSV に吐く場面が多い。出力するデータは PSCustomObject で作る、が定石です。
ここまでで分かったこと: PSCustomObject はプロパティを持つオブジェクト。記述順を保ち、表・CSV 出力に向く。
ハッシュテーブルと PSCustomObject の使い分け(CSV でハマる)
ここが業務でハマる2つ目。そして一番多い事故です。
「ハッシュテーブルもキーと値だし、そのまま CSV にできるだろ」と思って、こう書く。
$row = @{ Name = "田中"; Dept = "経理" }
$row | Export-Csv -Path "ng.csv" -NoTypeInformation
これ、バージョンによって壊れ方が違う。ただ、どっちにしても期待どおりにはならない。
- Windows PowerShell 5.1(
.NET Framework世代の現場に多いpowershell.exe)だと、Name・Deptが列になるんじゃなく、ハッシュテーブル自体の情報(CountやKeysなど)が列になって完全に崩れる - PowerShell 7 では改善されてキーは列になる。ところが今度は列の順序が保証されない。下の実測でも、書いた順は
Name, Deptなのに CSV はDept, Nameと逆順で出ています
実行結果(PowerShell 7 での実測):

どっちのバージョンでも、出力するデータは PSCustomObject で作るのが安全。記述した順のまま列が並びます。

覚え方はシンプル。「ひくだけならハッシュテーブル、出すなら PSCustomObject」。これだけ。
ここまでで分かったこと: ハッシュテーブルは CSV/表出力に向かない。出力するデータは PSCustomObject にする。
スプラッティング @params — 引数をまとめて渡す
最後。スプラッティングは、これまでの2つと毛色が違って、「コマンドへの引数の渡し方」です。中身はハッシュテーブルなんだけど、使い方が別。
引数の多いコマンドって、1行が横にどんどん伸びて読みにくい。
# 引数が多くて横に伸びる
Copy-Item -Path "C:\src\data.txt" -Destination "D:\backup\data.txt" -Force -Verbose
これを、引数をハッシュテーブルにまとめて、@ を付けて渡す。
$params = @{
Path = "C:\src\data.txt"
Destination = "D:\backup\data.txt"
Force = $true
Verbose = $true
}
Copy-Item @params
こんな感じで、横に伸びてた引数がすっきり縦にまとまる。
肝は、渡す時に $params じゃなく @params と書くこと。@ を付けると「このハッシュテーブルのキーを、そのまま名前付き引数として展開してね」の意味になる。ここが業務でハマる3つ目。$ のままだと「ハッシュテーブルそのものを1個の引数として渡す」ことになって動きません。ん?なんでエラー??ってなるやつなので、$ か @ かは最初に確認するクセをつけてください。
スプラッティングのいいところは、引数を条件で組み立てられることです。
$params = @{ Path = $src; Destination = $dst }
if ($force) { $params["Force"] = $true } # 条件次第で引数を足す
Copy-Item @params
if で -Force を付けたり外したり、を分岐だらけにせず書ける。1人運用のスクリプトだと、この組み立てやすさがかなり効く。
ここまでで分かったこと: スプラッティングは引数の渡し方。中身はハッシュテーブル、渡す時は
@を付ける。条件で引数を組み立てられる。
3つの使い分け早見
ここまでを一枚で。

判断に迷ったら、この順で辿ると早いです。

俺の現場ではこう使い分けてる
1人で AD の運用を回してた頃、ユーザーの棚卸しスクリプトで全部ハッシュテーブルに詰めて、最後に Export-Csv したら列が崩れて呆然とした。あれ、実際にやりました。半日は無駄にした記憶がある。
そこから使い分けをはっきりさせて、今はこう固定してます。
- 設定・接続情報 → ハッシュテーブル(
$config['ServerName']でひく) - 出力する1件のデータ → PSCustomObject(そのまま
Export-Csv/Format-Table) - コマンド呼び出し → スプラッティング(引数を組み立てて
@params)




