PowerShellのSelect-String検索は短い一行でも扱えますが、入力の種類や版を確認しないまま本番へ使うと誤判定を招きます。この記事の結論は「Select-Stringはfileまたはpipeline入力を行単位で検索し、MatchInfo objectを返します。literalなら-SimpleMatch、正規表現ならPattern、前後行は-Context、全一致は-AllMatchesを使い、Path・LineNumber・Matchesをobjectのまま扱います。」。regex検索と-SimpleMatchのliteral検索を分け、file encodingと大文字小文字を明示する場合を対象に、確認結果から次の行動を選べる形で解説します。
literal検索とregex検索を先に選ぶ
再帰検索のpathは先にfile一覧へ展開し、意図したlogだけをSelect-Stringへ渡します。Select-Stringがshell builtin、cmdlet、外部programのどれかも確認し、別実装のoptionを混在させません。
- Get-ItemとGet-Content -TotalCountで対象fileを確認する
- 検索語が正規表現かliteralかを決める
- Get-Help Select-Stringで版ごとのCaseSensitiveやEncodingを確認する
- 検索結果に秘密値が含まれない出力項目だけを選ぶ
ContextとAllMatchesの戻り値を読む
literalのERRORを検索
Select-String -LiteralPath 'C:\Logs\app.log' -SimpleMatch 'ERROR' |
Select-Object Path,LineNumber,Line
-SimpleMatchは正規表現meta文字を解釈しません。利用者入力をそのままregexにする必要がない場合の安全な既定です。
前後行を含める
Select-String -LiteralPath '.\app.log' -Pattern '\bERROR\b' -Context 2,3
-Contextは表示文脈を保持しますが、MatchInfoのLineNumberは一致行です。前後行を別eventとして数えません。
複数fileを再帰検索
Get-ChildItem -LiteralPath 'C:\Logs' -File -Filter '*.log' -Recurse |
Select-String -Pattern 'timeout|refused' |
Select-Object Path,LineNumber,Pattern,Line
先にGet-ChildItemで対象件数とpathを確認します。junctionや巨大treeを無制限に走査せずrootを限定します。
一行の全matchを取得
Select-String -LiteralPath '.\ids.txt' -Pattern '\bID-[0-9]{4}\b' -AllMatches |
ForEach-Object { $_.Matches.Value }
通常は一致行単位です。-AllMatchesで一行内の全matchを取得し、実際のValueを取り出します。
一致しない行を確認
Select-String -LiteralPath '.\records.txt' -SimpleMatch 'approved' -NotMatch
-NotMatchの結果だけを正本として保存すると必要行を失う恐れがあります。調査用表示または別copyに限定します。
再帰対象fileを列挙してから検索する
- literal文字列をregexとして解釈する
- Line表示だけを処理しobject propertyを捨てる
- Context行を一致件数へ含める
- 巨大drive全体を-Recurseする
- NotMatch結果で原本を上書きする
MatchInfoのPath・LineNumber・Matchesを保つ
Select-Stringの戻り値は文字列ではなくMatchInfoです。表示上は行だけに見えてもPath、Filename、LineNumber、Pattern、Matchesを持ちます。複数Patternはどれかに一致する行を返し、一行の複数一致を件数へ直す場合はMatches.Countを使います。PowerShellの既定case behaviorとculture差が重要なら-CaseSensitiveとsampleで明示します。
NotMatchの意味を反転条件として扱う
検索は読み取りですが、recursive検索はdisk I/Oを増やし、secretを含む行をconsoleやCSVへ集約します。root、拡張子、更新期間を先に限定し、結果には必要な列だけを残します。maskが必要なら正本を変更せずaccess制御されたcopyを作ります。検索結果をfileへ保存する場合は入力と別path、新規名、適切なACLを使い、誤出力はそのfileだけを削除して原本を維持します。
無効regexをcatchして0件と分ける
小さなsampleに一致0、1、一行複数、大小文字、regex meta文字を用意し、期待件数を比較します。PathとLineNumberから元fileを再読して一致行を照合し、result CountとMatches.Countを混同していないか確認します。監査用CSVはImport-Csvで読み戻し、機密項目と保持期限をreviewします。
match件数と対象file数を同時に検収する
固定文字列は-SimpleMatch、正規表現は-Patternへ分け、MatchInfoのPath、LineNumber、Line、Matchesを保持します。PowerShell cmdletは検索0件でも例外を投げないため、@($matches).Countで0件を明示し、file読取errorとは別にします。
ERROR行をMatchInfoとして収集する
$m=@(Select-String -LiteralPath '.\app.log' -SimpleMatch 'ERROR' -ErrorAction Stop)
[pscustomobject]@{Matches=$m.Count;Files=@($m.Path|Sort-Object -Unique).Count;FirstLine=$m[0].LineNumber}
一致・一致なし・pattern errorを分類する
- MatchInfo取得:MatchInfoが1件以上でPath/LineNumberが対象fileと一致し、AllMatchesなら一行内の個数も取得できる
- 一致行なし:正常な一致なしはCount 0で、後続処理が空arrayを受ける
- regex・file error:invalid regex、file不存在、access deniedはErrorAction Stopの例外として0件へ混ぜない
-NotMatchは『fileが無い』ではなくpatternに一致しない行を大量に返します。-Contextの表示文字列をparseせずContext.PreContext/PostContextを使い、複数fileの同名行番号をPathなしで集計しません。
角括弧と複数matchをfixtureにする
try { Select-String -InputObject 'abc' -Pattern '[' -ErrorAction Stop } catch { [pscustomobject]@{State='InvalidRegex';Message=$_.Exception.Message} }
0件、大小文字、literalの[]、regexの複数match、巨大一行、read不可fileをtestします。Count、Path、LineNumber、Matches.Count、例外categoryを期待表と比較します。
検索結果を文字列へ早期変換するとPathやLineNumberを失います。MatchInfoのまま件数を数え、0件と無効patternをErrorRecordの有無で切り分けてください。

コメント