PowerShellでログ内の文字列を検索する|Select-StringとSimpleMatchの違い

PowerShellでログ内の文字列を検索する|Select-StringとSimpleMatchの違い

ログの中からエラーを探したいのに、ファイルを開いて検索を繰り返していると、どの行に何が書かれていたかを追いにくくなります。PowerShellのSelect-Stringなら、一致した行の本文と行番号をまとめて取り出せます。

ただし、検索語のドットなどには特別な意味があります。文字をそのまま探す方法から始め、正規表現、大文字・小文字、結果の有無を使い分けると、意図しない一致を減らせます。以下はWindows PowerShell 5.1とPowerShell 7で使える書き方です。


文字列をそのまま検索する

UTF-8で保存したsample[1].logに、次の5行がある例で考えてみましょう。作業フォルダーへこの名前で保存すると、続くコマンドを試せます。

INFO start
ERROR a.b
error axb
WARN finish
ERROR repeat repeat

a.bという文字列をそのまま探すには、-SimpleMatchを付けます。ここでは結果を変数に入れ、行番号と本文だけを表示します。

$hits = Select-String -LiteralPath '.\sample[1].log' -Pattern 'a.b' -SimpleMatch -Encoding UTF8
$hits | Select-Object LineNumber, Line

この場合に一致するのは、2行目のERROR a.bです。LineNumberはファイル内の行番号、Lineは一致した行全体です。検索語だけに切り詰められるわけではないため、周囲のメッセージも読めます。

-LiteralPathは、角括弧などを含むファイル名をそのまま指定するための引数です。一方、-SimpleMatchは検索する文字列の解釈を変えます。ファイル名と検索語では、指定する場所が異なります。先にファイルの存在を調べたい場合は、Test-Pathでファイルの存在を確認する方法も参考になります。

正規表現で検索する

-SimpleMatchを外すと、検索語は正規表現として扱われます。たとえばドットは任意の1文字に一致するため、先ほどと同じa.bでも結果が変わります。

Select-String -LiteralPath '.\sample[1].log' -Pattern 'a.b' -Encoding UTF8 |
    Select-Object LineNumber, Line

2行目のa.bに加え、3行目のaxbも一致します。検索語に含まれる.、[、*などを文字として探すだけなら、-SimpleMatchを使うと意図が明確になります。

反対に、行頭のERRORだけに絞るなら、行頭を表す^が役立ちます。

Select-String -LiteralPath '.\sample[1].log' -Pattern '^ERROR ' -Encoding UTF8 |
    Select-Object LineNumber, Line

既定では大文字と小文字を区別しないため、サンプルの2・3・5行目が一致します。^もそのままの文字として探したい場合には-SimpleMatchを使うなど、探したい条件に合わせて選べます。

大文字と小文字を区別する

大文字のERRORだけを取り出したいときは、-CaseSensitiveを追加します。

Select-String -LiteralPath '.\sample[1].log' -Pattern 'ERROR' -SimpleMatch -CaseSensitive -Encoding UTF8 |
    Select-Object LineNumber, Line

この例では2・5行目が一致し、小文字のerrorがある3行目は結果に入りません。エラーを広く探す最初の段階では既定の検索を使い、表記まで条件に含めるときに追加すると扱いやすくなります。

一致する行があるか調べる

後続処理の条件に使うなら、-Quietで一致の有無を取得できます。

Select-String -LiteralPath '.\sample[1].log' -Pattern 'ERROR' -SimpleMatch -Quiet -Encoding UTF8
Select-String -LiteralPath '.\sample[1].log' -Pattern 'FATAL' -SimpleMatch -Quiet -Encoding UTF8

サンプルにはERRORがあり、FATALはないので、順に次の値が返ります。

True
False

-Quietなしで一致する行がない場合は、通常、結果オブジェクトが出力されません。ファイルが見つからないなどの読み取りエラーとは別です。エラーが出たときに「一致なし」と判断せず、まずパスやアクセス権を調べてください。

同じ行の複数の一致を取得する

1行の中に同じ語が何度あるかを調べたい場合は、-AllMatchesと結果のMatchesを使えます。

$hits = Select-String -LiteralPath '.\sample[1].log' -Pattern 'repeat' -AllMatches -Encoding UTF8
$hits | ForEach-Object { $_.Matches.Count }

5行目にはrepeatが2回あるため、出力は2です。-AllMatchesを付けても、一致する語の数だけ行が重複して返るわけではありません。また、-SimpleMatchと組み合わせた場合、-AllMatchesは無視されます。この例では正規表現の検索として実行しています。

結果が想定と違うときに見直す

  • 余計な行まで一致する場合は、検索語に正規表現の記号が含まれていないかを見ます。文字列そのままなら-SimpleMatchが適しています。
  • 大文字・小文字で結果を変えたい場合は、-CaseSensitiveの有無を見ます。
  • 文字が崩れる場合は、ファイルの保存時の文字コードに-Encodingを合わせます。ここで使ったサンプルはUTF-8です。
  • ファイルが見つからない場合は、現在の作業フォルダーとファイル名を確かめます。角括弧を含む名前には-LiteralPathが使えます。

ログを読むだけなら、ファイル全体を先に文字列へ変換せず、Select-Stringへパスを渡すところから始めると行番号も取得できます。ファイル全体を一つの文字列として処理したい場合は、Get-Content -Rawと行ごとの読み取りの違いを用途に合わせて使ってください。

参考資料

このブラウザからの自分のアクセスを解析から除外できます。