【PowerShell】ファイルの中身から文字列を検索する方法|Select-String(grep相当)

【PowerShell】ファイルの中身から文字列を検索する方法|Select-String(grep相当) PowerShell

「このフォルダのログファイルの中から、ERRORという文字を含む行を探したい」「設定ファイルの中で特定のキーワードを使っている箇所を見つけたい」——こうしたファイルの中身(テキスト)に対する検索は、PowerShellのSelect-Stringで行えます。LinuxやMacのgrepに相当するコマンドで、複数ファイルをまとめて検索し、ヒットした行を行番号付きで表示できます。

注意したいのは、-Patternに指定する文字列は正規表現として扱われることです。.(ドット)や*などの記号は特別な意味を持つため、文字どおりに検索したいときは-SimpleMatchを使います。また、-Path "*.log"のようなワイルドカード指定はサブフォルダまで検索しません。この記事では、実機のPowerShell 5.1で確認しながら、Select-Stringを整理します。

先に結論

  • 基本はSelect-String -Path "ファイル" -Pattern "検索語"です。
  • -Patternは正規表現。記号を文字どおり探すなら-SimpleMatchを使います。
  • 大文字小文字は区別しないのが既定。区別するなら-CaseSensitive
  • サブフォルダも検索するにはGet-ChildItem -Recurse | Select-Stringとします。
  • 一致しない行は-NotMatch、各ファイル1件だけは-List、前後の行は-Context
  • 結果からは.Filename.LineNumber.Lineを取り出せます。

-Patternの正規表現は正規表現で文字列を検索・置換する、対象ファイルの取得はファイル・フォルダ操作、結果の絞り込みはWhere-Object・Select-Objectもあわせて参考になります。

スポンサーリンク

基本:Select-Stringで文字列を探す

もっとも基本的な使い方は、-Pathで検索対象のファイル、-Patternで検索したい文字列を指定します。ヒットした行が、ファイル名と行番号つきで表示されます。

基本の検索
# ログファイルから ERROR を含む行を探す
Select-String -Path "C:\logs\app.log" -Pattern "ERROR"

# 複数ファイルをまとめて検索(ワイルドカード)
Select-String -Path "C:\logs\*.log" -Pattern "ERROR"

# 出力例: ファイル名:行番号:ヒットした行
# app.log:2:ERROR disk full

実機でも、ログファイルに対して-Pattern "error"で検索すると、ERROR disk fullの行がファイル名:行番号:行の内容の形式で表示されました。大文字小文字は既定で区別しないため、errorで検索してもERRORがヒットします。複数ファイルを*.logのように指定すれば、まとめて検索できます。

Patternは正規表現(リテラルは-SimpleMatch)

ここが重要な注意点です。-Patternに指定した文字列は正規表現として解釈されます。たとえば.(ドット)は「任意の1文字」を意味するため、3.14で検索すると3X14のような文字列にもヒットしてしまいます。記号を文字どおりに検索したいときは-SimpleMatchを付けます。

正規表現とリテラル検索
# -Pattern は正規表現。. は「任意の1文字」になる
Select-String -Path "data.txt" -Pattern "3.14"
#   → "3.14" だけでなく "3X14" や "3914" にもヒットする

# 文字どおり "3.14" を探したいなら -SimpleMatch
Select-String -Path "data.txt" -Pattern "3.14" -SimpleMatch
#   → 文字どおりの "3.14" だけにヒット
記号を含む検索は -SimpleMatch

実機で確認したところ、version 3X14 hereという行に対して、正規表現の-Pattern "3.14"ヒットしました.が任意の1文字としてXに一致)。一方、-SimpleMatchを付けた"3.14"ヒットしませんでした(文字どおりの3.14でないため)。IPアドレス・バージョン番号・ファイルパス・金額など、.\*(などの記号を含む文字列をそのまま検索したいときは、-SimpleMatchを使うのが安全です。逆に、正規表現のパターンマッチを活かしたいときは-SimpleMatchを付けずに使います。

サブフォルダも検索する(再帰)

気をつけたいのは、-Path "*.log"のようなワイルドカード指定では、サブフォルダの中までは検索しないことです。フォルダ全体を再帰的に検索するには、Get-ChildItem -Recurseでファイルを集めてSelect-Stringに渡します。

サブフォルダを含む再帰検索
# サブフォルダも含めて .log を検索する(grep -r 相当)
Get-ChildItem "C:\logs" -Recurse -Filter *.log |
    Select-String -Pattern "ERROR"

# 特定の拡張子に絞らず全ファイルから探すなら
Get-ChildItem "C:\project" -Recurse -File |
    Select-String -Pattern "TODO" 

実機でも、Get-ChildItem -Recurse -Filter *.log | Select-String -Pattern "ERROR"とすると、トップのファイルとサブフォルダ内のファイルの両方から該当行を検出できました(合計2件)。一方、-Path "*.log"だけではトップのファイルしか検索されません。「サブフォルダの中まで探したのに見つからない」というときは、Get-ChildItem -Recurseと組み合わせているかを確認してください。-Filterで拡張子を絞ると高速になります。

大文字小文字・一致しない行

大文字小文字を区別して検索するには-CaseSensitive、逆に検索語を含まない行を抽出するには-NotMatchを使います。

大小区別・除外検索
# 大文字小文字を区別する("ERROR" と "error" を別物として扱う)
Select-String -Path "*.log" -Pattern "error" -CaseSensitive

# 検索語を「含まない」行だけを抽出する
Select-String -Path "data.txt" -Pattern "DEBUG" -NotMatch
#   → DEBUG を含まない行だけが残る(ノイズ除去に便利)

実機でも、-CaseSensitiveを付けて小文字のerrorを検索すると、大文字のERRORはヒットしなくなりました(一致0件)。また-NotMatchを使うと、DROPを含む行を除いたkeep1keep2の2行だけが残りました。-NotMatchは、特定のノイズ行(DEBUGやコメント行など)を除いて中身を確認したいときに便利です。

各ファイル1件だけ・前後の行

大量にヒットすると見づらいので、各ファイルで最初の1件だけに絞る-Listや、ヒット行の前後の行も一緒に表示する-Contextが役立ちます。

-List と -Context
# 各ファイルで最初にヒットした1件だけ表示(どのファイルに有るか確認)
Get-ChildItem "C:\logs" -Recurse -Filter *.log |
    Select-String -Pattern "ERROR" -List

# ヒット行の前2行・後2行も一緒に表示(前後の文脈を確認)
Select-String -Path "app.log" -Pattern "ERROR" -Context 2, 2

実機でも、-Listを付けると、ERRORを含む2つのファイルそれぞれから最初の1件だけ(合計2件)が表示されました。「どのファイルにキーワードが含まれるか」をざっと知りたいときに便利です。-Context 2, 2は、ヒット行の前2行・後2行も一緒に表示するので、エラーの前後で何が起きていたかを確認できます。

結果を活用する(ファイル名・行番号)

Select-Stringの結果はただの文字列ではなく、ファイル名や行番号などの情報を持つオブジェクトです。.Filename.LineNumber.Lineを取り出して、さらに加工できます。

結果のプロパティを使う
$hits = Select-String -Path "C:\logs\*.log" -Pattern "ERROR"

# ファイル名・行番号・該当行を整形して表示
$hits | ForEach-Object {
    Write-Host "$($_.Filename) の $($_.LineNumber) 行目: $($_.Line)"
}

# ヒット件数を数える
$hits.Count

# ヒットしたファイル名の一覧(重複を除く)
$hits | Select-Object -ExpandProperty Filename -Unique

.Filenameはファイル名、.LineNumberは行番号、.Lineはヒットした行の内容です。.Pathでフルパスも取れます。これらを使えば、「どのファイルの何行目に問題があるか」を一覧にしたり、.Countでヒット件数を数えたりできます。日本語を含む行も問題なく検索・取得できることを実機で確認しています。

主なオプション

Select-Stringでよく使うオプションをまとめます。

オプション 働き
-Pattern 検索する文字列(正規表現)
-Path 検索対象のファイル(ワイルドカード可)
-SimpleMatch 正規表現でなく文字どおり検索する
-CaseSensitive 大文字小文字を区別する
-NotMatch 一致しない行を抽出する
-List 各ファイルで最初の1件だけ
-Context 前, 後 ヒット行の前後も表示する

よくある失敗

記号を含む検索でヒットしすぎる

-Patternは正規表現です。記号を文字どおり探すなら-SimpleMatchを使います。

サブフォルダが検索されない

-Path "*.log"は再帰しません。Get-ChildItem -Recurse | Select-Stringとします。

大文字小文字が区別されない

既定では区別しません。区別したいときは-CaseSensitiveを付けます。

結果を文字列として扱おうとする

結果はオブジェクトです。.Line.LineNumber.Filenameで取り出します。

ヒットが多すぎて見づらい

-Listで各ファイル1件に絞るか、-NotMatchでノイズ行を除きます。

よくある質問

QPowerShellでgrepのようにファイルの中を検索できますか?
ASelect-Stringgrepに相当します。Select-String -Path "*.log" -Pattern "ERROR"で、ファイルの中身からキーワードを含む行を行番号つきで検索できます。サブフォルダも含めるならGet-ChildItem -Recurse | Select-String -Pattern "ERROR"とします。
Q検索したい文字に . や * が含まれます。
A-Patternは正規表現なので、.*は特別な意味を持ちます。文字どおりに検索したいときは-SimpleMatchを付けてください。IPアドレスやバージョン番号、ファイルパスなど記号を含む文字列の検索では、-SimpleMatchが安全です。
Qサブフォルダの中まで検索するには?
A-Pathのワイルドカードは再帰しないため、Get-ChildItem "フォルダ" -Recurse -Filter *.log | Select-String -Pattern "検索語"のように、Get-ChildItem -Recurseで集めたファイルを渡します。-Filterで拡張子を絞ると高速です。
Q大文字小文字を区別して検索するには?
ASelect-Stringは既定で大文字小文字を区別しません。区別したいときは-CaseSensitiveを付けてください。これによりERRORerrorを別の文字列として扱えます。
Q検索結果から行番号やファイル名を取り出すには?
ASelect-Stringの結果はオブジェクトで、.Filename(ファイル名)、.LineNumber(行番号)、.Line(ヒットした行)、.Path(フルパス)を持ちます。$hits | ForEach-Object { "$($_.Filename):$($_.LineNumber)" }のように取り出して加工できます。

まとめ

  • ファイルの中身検索はSelect-String -Path "ファイル" -Pattern "検索語"(grep相当)。
  • -Patternは正規表現。記号を文字どおり探すなら-SimpleMatch
  • サブフォルダはGet-ChildItem -Recurse | Select-Stringで再帰検索します。
  • 大小区別は-CaseSensitive、除外は-NotMatch、各1件は-List
  • 結果は.Filename.LineNumber.Lineで取り出せます。

Select-Stringを使えば、PowerShellだけでファイルの中身を高速に検索できます。「-Patternは正規表現」「再帰はGet-ChildItem -Recurseと組み合わせる」の2点さえ押さえれば、ログ調査やソースコード内のキーワード検索を効率よく行えます。grepに慣れた人も、すぐに使いこなせるはずです。