【PowerShell】Where-Object・Select-Object・Sort-Objectでデータを絞り込む・選ぶ・並べる

【PowerShell】Where-Object・Select-Object・Sort-Objectでデータを絞り込む・選ぶ・並べる PowerShell

PowerShellの真骨頂は、パイプライン(|)でデータを次々と加工していけることです。その中心になるのが、絞り込むWhere-Object列を選ぶSelect-Object並べ替えるSort-Objectの3つです。Get-ProcessImport-Csvで取り出したデータを、この3つで自在に整形できます。

使い方は素直ですが、特にSelect-ObjectにはSelect-Object Name-ExpandProperty Nameで結果がまったく違うという重要な落とし穴があります。前者はオブジェクト、後者はその値そのものが返ります。これを知らないと、取り出した値が文字列として使えず悩むことになります。この記事では、実機で確認しながら3つの使い方を整理します。

先に結論

  • Where-Object { $_.Price -ge 300 }で条件に合う行だけに絞り込みます。簡略構文Where-Object Price -ge 300も使えます。
  • Select-Object Name, Priceで必要な列(プロパティ)だけ選べます。-First-Last-Uniqueも便利です。
  • Select-Object Nameはオブジェクト、-ExpandProperty Nameは値そのものが返ります。値を直接使うなら後者です。
  • @{ Name="列名"; Expression={ ... } }で計算した新しい列を作れます。
  • Sort-Object Price -Descendingで並べ替え。複数キーや-Uniqueも指定できます。
  • 文字列に入った数値は辞書順になります("10""9"より前)。数値順ならSort-Object { [int]$_ }です。

絞り込み条件で使う比較演算子は比較演算子と条件分岐、扱うデータの土台は配列とハッシュテーブルの使い方、CSVでの実例は条件一致したデータだけを抽出する方法もあわせて参考になります。

スポンサーリンク

サンプルデータを用意する

この記事では、次の商品リストを例に使います。PSCustomObjectの配列で、各商品が名前・価格・在庫を持っています。

sample-data.ps1
$products = @(
    [PSCustomObject]@{ Name = "りんご"; Price = 300; Stock = 5 }
    [PSCustomObject]@{ Name = "みかん"; Price = 100; Stock = 20 }
    [PSCustomObject]@{ Name = "ぶどう"; Price = 800; Stock = 3 }
    [PSCustomObject]@{ Name = "バナナ"; Price = 200; Stock = 20 }
)

Where-Objectで絞り込む

条件に合う行だけを残すのがWhere-Objectです。{ }の中に条件を書き、現在の行を$_で参照します。エイリアスは?です。

where-object.ps1
# スクリプトブロック構文($_ で各行を参照)
$products | Where-Object { $_.Price -ge 300 }
# りんご(300), ぶどう(800)

# 複数条件は -and / -or で
$products | Where-Object { $_.Price -lt 500 -and $_.Stock -ge 10 }
# みかん, バナナ

# 簡略構文(1つの条件なら { } や $_ を省略できる)
$products | Where-Object Price -ge 300
# りんご, ぶどう

実機でも、Where-Object { $_.Price -ge 300 }と簡略構文Where-Object Price -ge 300はどちらもりんご, ぶどうを返しました。条件が1つなら簡略構文がすっきり書けますが、-andで複数条件を組み合わせるときはスクリプトブロックを使います。

Select-Objectで列(プロパティ)を選ぶ

必要な列だけを取り出すのがSelect-Objectです。プロパティ名を並べると、その列だけを持つオブジェクトになります。件数を絞る-First-Last-Skipや、重複を除く-Uniqueもあります。

select-object.ps1
# 必要な列だけ選ぶ
$products | Select-Object Name, Price

# 先頭・末尾・スキップ
$products | Select-Object -First 2    # 先頭2件
$products | Select-Object -Last 1     # 末尾1件
$products | Select-Object -Skip 2     # 先頭2件を飛ばす

# 重複を除く(在庫数の種類を知りたいなど)
$products | Select-Object -ExpandProperty Stock | Sort-Object -Unique
# 3, 5, 20

-Firstは、必要な件数が取れた時点でパイプラインを止めるため、大量データから先頭だけ欲しいときに効率的です。

【最重要】Select-Object と -ExpandProperty の違い

ここがもっとも間違えやすいポイントです。Select-Object NameSelect-Object -ExpandProperty Nameは、似ているのに返ってくるものが違います

expand-property.ps1
# Select-Object Name → 「Name プロパティを持つオブジェクト」が返る
$products | Select-Object Name
# @{Name=りんご}, @{Name=みかん}, ... ← 文字列ではない

# -ExpandProperty Name → 「Name の値(文字列)」そのものが返る
$products | Select-Object -ExpandProperty Name
# りんご, みかん, ぶどう, バナナ ← そのまま文字列として使える

# 型を見ると違いが分かる
($products | Select-Object Name)[0].GetType().Name              # PSCustomObject
($products | Select-Object -ExpandProperty Name)[0].GetType().Name  # String
値そのものが欲しいなら -ExpandProperty

実機で確認したところ、Select-Object Nameの結果はPSCustomObject(Nameプロパティを持つオブジェクト)、-ExpandProperty Nameの結果はString(値そのもの)でした。取り出した名前を別のコマンドに渡したり、-joinで連結したり、文字列として比較したりするなら、-ExpandPropertyで値を取り出す必要があります。Select-Object Nameのまま使うと「オブジェクトが返ってきて文字列にならない」と悩むことになります。

計算プロパティで新しい列を作る

元のデータにない列を、計算して作ることもできます。@{ Name="列名"; Expression={ 計算 } }という書き方(計算プロパティ)をSelect-Objectに渡します。

calculated-property.ps1
# 税込価格の列を追加する
$products | Select-Object Name, Price, @{
    Name = "税込"
    Expression = { [int]($_.Price * 1.1) }
}
# りんご 300 330 / みかん 100 110 / ...

# 短縮キー(n / e)でも書ける
$products | Select-Object Name, @{ n="在庫評価額"; e={ $_.Price * $_.Stock } }

実機でも、りんご(価格300)の税込列が330になりました。Name(列名)とExpression(計算式)はそれぞれneと短縮しても書けます。Expressionの中では、各行を$_で参照します。

Sort-Objectで並べ替える

並べ替えはSort-Objectです。プロパティ名を指定すると、その値で昇順に並びます。-Descendingで降順、カンマ区切りで複数キー、-Uniqueで重複除去ができます。

sort-object.ps1
# 価格で昇順
$products | Sort-Object Price
# みかん(100), バナナ(200), りんご(300), ぶどう(800)

# 価格で降順
$products | Sort-Object Price -Descending
# ぶどう, りんご, バナナ, みかん

# 複数キー(在庫→価格の順で並べる)
$products | Sort-Object Stock, Price
# ぶどう(3), りんご(5), みかん(20/100), バナナ(20/200)

実機でも、Sort-Object Stock, Priceは在庫数で並べ、在庫が同じ(みかんとバナナはどちらも20)場合は価格で並べました。複数キーは、左に書いたものが優先されます。

【注意】文字列の数値は辞書順になる

Sort-Objectでハマりやすいのが、数値が文字列として入っている場合です。CSVから読み込んだデータなどは、数字に見えても中身は文字列のことがあります。文字列のままソートすると、数の大小ではなく辞書順(1文字ずつの比較)になります。

sort-string-number.ps1
$strNums = "9", "10", "100", "2"

# 文字列のままだと辞書順("1" < "2" < "9" の順で先頭を比較)
$strNums | Sort-Object
# 10, 100, 2, 9 ← 数の順になっていない!

# スクリプトブロックで数値に変換してからソートする
$strNums | Sort-Object { [int]$_ }
# 2, 9, 10, 100 ← 正しい数値順

実機でも、文字列"9","10","100","2"をそのままソートすると10, 100, 2, 9という辞書順になりました。数値順に並べたいときは、Sort-Object { [int]$_ }のように数値へ変換するスクリプトブロックを渡します。オブジェクトのプロパティならSort-Object { [int]$_.列名 }です。

3つを組み合わせる実践パイプライン

実務では、この3つを|でつないで使います。「絞り込む → 並べ替える → 必要な分だけ選ぶ」という流れが基本です。

combined-pipeline.ps1
# 在庫が5以上の商品を、価格の高い順に、上位2件だけ表示する
$products |
    Where-Object { $_.Stock -ge 5 } |
    Sort-Object Price -Descending |
    Select-Object Name, Price -First 2
# りんご(300), バナナ(200)

# プロセスのCPU上位5件(実例)
Get-Process |
    Where-Object CPU -gt 10 |
    Sort-Object CPU -Descending |
    Select-Object Name, CPU -First 5

パイプラインは、左から右へデータが流れていきます。順番は「絞る(Where)→ 並べる(Sort)→ 選ぶ(Select)」が自然です。Select-Object -Firstを最後に置くと、並べ替えた結果の上位だけを取り出せます。長くなるときは、行末で|を使って改行すると読みやすくなります。

よくある失敗

Select-Objectの結果が文字列にならない

Select-Object Nameはオブジェクトを返します。値そのものが欲しいなら-ExpandProperty Nameを使います。

文字列の数値が数の順に並ばない

CSVなどから読んだ数字は文字列のことがあります。辞書順になってしまうので、Sort-Object { [int]$_ }で数値に変換してから並べます。

Where-Objectで $_ を付け忘れる

スクリプトブロック構文では、プロパティに$_.を付けます。{ Price -ge 300 }ではなく{ $_.Price -ge 300 }です。簡略構文なら$_は不要です。

絞り込みより先に件数を絞ってしまう

Select-Object -FirstSort-Objectより前に置くと、並べ替える前の先頭が取られます。「絞る→並べる→選ぶ」の順を意識してください。

計算プロパティのキー名を間違える

計算プロパティのキーはName(またはn)とExpression(またはe)です。綴りを間違えると列名が付かなかったり計算されなかったりします。

よくある質問

QSelect-Object と -ExpandProperty はどう違いますか?
ASelect-Object Nameは「Nameプロパティを持つオブジェクト」を返し、-ExpandProperty Nameは「Nameの値そのもの(文字列など)」を返します。取り出した値を別のコマンドに渡したり連結したりするなら-ExpandPropertyを使います。
QWhere-Objectの簡略構文とスクリプトブロックはどちらがいいですか?
A条件が1つだけならWhere-Object Price -ge 300の簡略構文が短くて読みやすいです。-and-orで複数条件を組み合わせるときは、Where-Object { $_.A -gt 1 -and $_.B -lt 5 }のスクリプトブロックを使います。
Q数字が正しい順に並びません。
Aその列が文字列になっている可能性が高いです。Sort-Object { [int]$_.列名 }のように数値へ変換してからソートしてください。CSVから読み込んだ値は文字列であることが多いです。
Q新しい列(合計や税込など)を追加するには?
A計算プロパティ@{ Name="列名"; Expression={ 計算式 } }Select-Objectに渡します。Expressionの中で各行を$_で参照し、$_.Price * 1.1のように計算します。
Q重複を取り除くには?
ASort-Object -UniqueまたはSelect-Object -Uniqueを使います。値そのものの重複を消すなら、-ExpandPropertyで値を取り出してからSort-Object -Uniqueすると確実です。

まとめ

  • Where-Objectで絞り込みます。条件1つなら簡略構文、複数なら{ $_.X ... }のスクリプトブロックです。
  • Select-Objectで列を選びます。-First-Last-Uniqueも便利です。
  • Select-Object Nameはオブジェクト、-ExpandProperty Nameは値そのものが返ります。
  • 計算プロパティ@{ Name=...; Expression={ ... } }で新しい列を作れます。
  • Sort-Objectで並べ替えます。文字列の数値は辞書順になるので{ [int]$_ }で数値順にします。
  • 実務では「絞る → 並べる → 選ぶ」の順でパイプラインをつなぎます。

この3つは、PowerShellでデータを扱うときに最も多用する組み合わせです。Where-ObjectSort-ObjectSelect-Objectを流れるようにつなげられるようになると、ログ集計や一覧の整形が一気に楽になります。特に-ExpandPropertyだけは、早めに覚えておくとつまずきません。