PowerShellのWhere-Objectコマンドでデータを効率的にフィルタリングする方法

Where-Objectによるobject filterでは、画面に値が出たことと目的を満たしたことを分けて考えます。結論は「Where-Objectはpipeline objectを条件式で残すcommandです。まずGet-Memberと少量sampleでproperty・型・nullを確認し、比較演算子を選びます。大量取得はsource側filter parameterがあればそちらを優先します。」。文字列行ではなくPowerShell objectのpropertyと型を確認してfilterする条件のもと、出力の意味、境界値、影響のある操作を順に確認します。

目次

source側filterを使えるか先に確認する

Where-Objectは文字列の行ではなくPowerShellオブジェクトを絞り込むため、入力を先頭数件だけ表示して型と対象プロパティを確認します。比較値の型をそろえ、null、空文字、数値ゼロを混同しない条件にします。入力件数と抽出件数を記録し、想定より広い結果なら変更系コマンドへ接続しません。

  • Select-Object -First 3とGet-Memberで型を確認する
  • filter対象propertyにnullや配列があるか確認する
  • -eq、-like、-match、-containsの意味を分ける
  • source cmdletに-Filter等のserver-side絞込がないかhelpを読む

簡易構文とscript blockを使い分ける

service Statusをfilter

Get-Service | Where-Object Status -eq 'Running' |
  Select-Object Name,DisplayName,Status

簡略syntaxは単一property比較に読みやすい形です。Runningだからapplication正常とは断定しません。

file sizeとextensionを組み合わせる

Get-ChildItem -LiteralPath 'C:\Logs' -File |
  Where-Object { $_.Extension -eq '.log' -and $_.Length -gt 10MB } |
  Select-Object FullName,Length,LastWriteTime

Lengthはbyteです。先にrootを限定し、再帰は必要時だけ追加します。

nullを明示してprocess CPUを見る

Get-Process | Where-Object { $null -ne $_.CPU -and $_.CPU -gt 60 } |
  Select-Object ProcessName,Id,CPU

CPUは累積秒です。nullと0を同じにせず、瞬間使用率として扱いません。

正規表現とliteral wildcardを分ける

Get-Process | Where-Object ProcessName -like 'power*'
Get-Process | Where-Object ProcessName -match '^power(shell|sh)$'

-likeはwildcard、-matchはregexです。利用者入力を未検証でregexへ入れません。

source側filterを優先

Get-WinEvent -FilterHashtable @{LogName='System';Id=7036;StartTime=(Get-Date).AddHours(-1)}

大量eventを全件取得してWhere-Objectで絞るより、provider側filterで取得量を減らします。

数値・文字列・enumを型のまま比較する

Where-Object script block内の$_は現在objectです。propertyがcollectionなら-eqが一致要素を返す場合があり、boolean条件と出力の違いに注意します。数値と文字列を暗黙変換すると左辺型で結果が変わり得るため、型を揃えます。filter後Countだけでなく対象sampleを確認し、0件をerrorにするか仕様化します。

null・数値0・文字列0を分離する

filter前後のMeasure-Object Count、先頭・末尾sample、除外された既知objectを比較します。境界値、null、大小文字、空collectionをtest dataで通し、script blockをPester等で再現可能にします。変更手順では対象一覧hashを確定してから実行し、実行後に同じqueryで期待状態を確認します。

regexとwildcardの一致規則を選ぶ

  • 文字列と数値の型を見ない
  • -likeと-matchを混同する
  • nullを0として扱う
  • server-side filterを使わず全件取得する
  • filter結果を即変更commandへpipeする

Format commandはpipeline末尾に置く

Where-Object自体は読み取りですが、結果をStop、Remove、Set系へpipeするとfilter誤りが大量変更へ直結します。最初はSelect-ObjectでID、path、件数を表示し、上限と除外条件をreviewします。変更前snapshotとbackupを作り、WhatIf対応なら利用します。不合格なら変更を開始せず、変更済みの場合は対象IDごとの元値から戻します。

入力件数と選択件数でfilterを検収する

Where-Objectはpipelineへ到達したobjectだけをclient-sideで評価します。property比較の簡略構文とscriptblockを使い分け、null、数値、DateTime、wildcard、regexのoperatorを型に合わせます。大量sourceではGet-WinEvent等のsource-side filterを先に使います。

CPU値を持つprocessだけobjectのまま絞る

$all=@(Get-Process)
$selected=@($all | Where-Object { $null -ne $_.CPU -and $_.CPU -gt 60 })
[pscustomobject]@{Input=$all.Count;Selected=$selected.Count;MinCpu=($selected.CPU|Measure-Object -Minimum).Minimum}

0件と式評価失敗を入力件数から見抜く

  • objectを選択:入力件数とfilter後件数を記録し、期待property型で条件を満たすobjectだけ残る
  • 条件一致なし:正常な0件はempty arrayとして条件を表示し、null inputとは別にする
  • property・expression error:property不存在、scriptblock例外、暗黙変換errorはErrorVariable等で集計する

Format-Table後の表示objectをfilterせず、Where-Objectの前でSelect-Objectにより必要propertyを落としません。-likeのwildcardと-matchのregexを混同し、ProcessNameがpwshなのに不正なpatternで除外しないようsampleを置きます。

nullと異なる0値をfixtureにする

[pscustomobject]@{Value=$null},[pscustomobject]@{Value=0},[pscustomobject]@{Value='0'} | Where-Object { $null -ne $_.Value } | Select-Object Value,@{n='Type';e={$_.Value.GetType().Name}}

null/0/’0’、propertyなし、大小文字、wildcard記号、regex errorをtestします。入力/出力件数、型、filter式、実行時間を保存し、server-side版と件数を照合します。

Where-Object後の0件は正常なfilter結果になり得ます。入力件数、選択件数、比較したproperty型を残し、source commandがfilterを持つ場合は取得段階で絞る方を優先します。

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次