こんにちは、PowerShellが好きなwakです。以前書いたこちらの記事をリライトします。
はじめに
Windowsにはgrepコマンドがないとはよく言われることですが、Windowsに標準で備わっているPowerShellにはgrepよりも高性能な検索コマンドレット、Select-Stringが実装されています。この記事はgrepとSelect-Stringとを比較し、慣れない人でもすぐに使えるようにすることを目的としています。また、「そもそもこのSelect-Stringコマンドをどうやって実行するの?」といったPowerShellの(超)基本的な使い方は本稿の末尾に記載してあります。
grepとの比較表
| grep | Select-String | |
|---|---|---|
| 大文字・小文字の区別 | -i | -CaseSensitive *1 |
| 正規表現 | -E | -SimpleMatch *2 |
| マッチしない行を検索 | -v | -NotMatch |
| 前後表示 | -A 《行数》 -B 《行数》 |
-Context 《行数》 |
| 文字コード指定 | 不可*3 | -Encoding 《文字エンコーディング名》 |
| ディレクトリを 再帰的に検索 |
-r | (dir -recurse 《ファイル名パターン》) |
| ファイル名のみ出力 | -l -L |
(後述) |
以下、順に解説していきます。
1件のファイルから検索をする場合
grepで1件のファイルから指定したフレーズを検索する場合はこのように実行しますが、
grep pattern filename.txt
PowerShellでこちらに相当する処理はこうなります。
Select-String "pattern" filename.txt
Select-Stringにはslsという省略形が用意されています。エイリアスですのでどちらを使ってもまったく同じ結果が得られます。
sls "pattern" filename.txt
本稿では以降もSelect-Stringと表記しますが、もちろん実際に使うときにはslsで構いません。
-i : 大文字・小文字を区別したい/区別したくない
デフォルトでは大文字・小文字は区別されません。区別したい場合は-CaseSensitiveオプションを追加します。
# "Neko"にはマッチするが"neko"にはマッチしない Select-String "Neko" filename.txt -CaseSensitive
-e : 正規表現を使いたい/使いたくない
パターンはデフォルトで正規表現とみなされます。正規表現として扱ってほしくない場合は-SimpleMatchオプションを追加します。
# "SELECT * FROM"にマッチ。-CaseSensitiveも指定しているので小文字ならマッチしない Select-String "SELECT * FROM" filename.txt -SimpleMatch -CaseSensitive
-v : パターンにマッチしない行を探したい
-NotMatchオプションを使います。
# 「A」「a」「B」「b」「C」「c」のいずれも含まない行にマッチ Select-String "[A-C]" filename.txt -NotMatch
-A / -B : パターンにマッチした前後の行も出力したい
-Contextオプションで行数を指定します。残念ながら前後別々に行数を指定することはできません。
# "function", "Function"などがある行と、その上下3行ずつ(計7行)を検索 Select-String "function" HogeClass.cs -Context 3
文字エンコーディングを指定したい
検索対象のファイルがShift-JISだと文字エンコーディングの自動判定ができないので、明示的に指定してあげなければいけません。これは-Encodingオプションを使います。
Select-String "猫" filename.txt -Encoding oem
-Encoding(gの後ろにスペース1個)と入力した後でTABキーを押せば指定可能な文字エンコーディングが順に表示されます。oemがShift-JISだと覚えておけば事足りるでしょう(UTF-8がデフォルトです)。
複数のファイルから検索する場合
ここまでは1件のファイルから検索を行うものでした。次は複数件のファイルを対象として検索を行う方法です。
ワイルドカードを使いたい
まず普通にワイルドカードが使えます。
Select-String "pattern" *.txt
特定の複数のファイルを指定して検索したい
ワイルドカードではなく、ファイル名を書き並べたいならこうなります。カッコとダブルクオートが少々見づらいのですが我慢してください。
Select-String "pattern" ("z:\log\filename1.txt", "c:\data\text\filename2.txt")
ディレクトリを再帰的にたどって検索したい
次のどちらでも好きなものを選んでください。c:\data以下にある*.cs全てを検索対象としています。
Select-String "pattern" (dir -recurse c:\data\*.cs) dir -recurse c:\data\*.cs | Select-String "pattern"
Dir -recurse c:\data\*.csは、c:\data以下から*.csを全部探すという意味になります(1回このコマンドだけを実行してみるといいです)。ディレクトリ名を省略したらカレントディレクトリになります。
特定の拡張子のファイルは除外したい
-Excludeで除外できます。
# カレントディレクトリ以下の全てのファイルから検索、ただし*.exeと*.binは除く Select-String "pattern" (dir -recurse *.* -Exclude *.exe, *.bin) dir -recurse *.* -Exclude *.exe, *.bin | Select-String "pattern"
検索対象のファイルを別のテキストファイルから与えたい
1行に1件ファイル名(フルパス)が書いてあるテキストファイルfilelist.txtがあったとして、そこから検索をする場合です。
Get-Content filelist.txt -Encoding UTF8 | % { Select-String "pattern" $_ }
なお、カレントディレクトリ以下の全てのファイル名(フルパス)をファイルに書き出すにはこのように実行します。ワイルドカードの部分は必要に応じて*.txtなどと書き換えてください。
dir -Recurse -File *.* | % { $_.FullName } | Out-File filelist.txt -Encoding UTF8
出力の書式や出力先を変える
標準の出力の書式では、ファイル名・行数・行の内容(行全体)が出力されます。なぜかというと、この結果1行1行はそれぞれMicrosoft.PowerShell.Commands.MatchInfoクラスのインスタンスであり、このToString()メソッドがそのような書式の文字列を返すようになっているからです。
https://msdn.microsoft.com/ja-jp/library/microsoft.powershell.commands.matchinfo(v=vs.85).aspx
このオブジェクトはFileName, Lineといったプロパティを持っていますから、欲しいものを好きなように並べて結果を出力することができます。
結果だけを画面に出力したい
ファイル名はいらない場合です。
# 末尾に「| Select Line」と書き加える Select-String "pattern" filename | Select Line Select-String "pattern" (dir -recurse *.* -Exclude *.exe, *.bin) | Select Line
以下では例は1行ずつしか示しませんが、どのような検索を行ったかにかかわらず、検索を行ったコマンドの末尾に「| ~」と書き加える形でOKです。
結果だけを画面に出力したい
ファイル名はいらない場合です。
Select-String "pattern" filename | Select Line
少し書式は面倒ですが、こちらの方を使うと色々と融通がききます。
Select-String "pattern" filename | % { $_.Line }
マッチした部分だけを出力したい
Select-String "pattern" filename | % { $_.Matches.Value }
このMatchesは.NETのSystem.Text.RegularExpressions.Matchクラスのインスタンスです。パターンに()を使っていればGroupsプロパティでさらに部分文字列を取り出せたりします。*4
Select-String "^(\d{4})-(\d{2})-(\d{2})" (dir -Recurse *.*) | % { $g = $_.Matches.Groups; $g.Groups[1].Value }
ファイル名(フルパス)、行数、行の内容をタブ区切りで出力したい
Select-String "pattern" filename | % { [string]::Format("{0}`t{1}`t{2}", $_.Path, $_.LineNumber, $_.Line) }
.NETを使っている人にはお馴染みのSystem.String.Format()を使ってみました。`tは他言語の\tと同じタブ文字を意味します。PowerShellらしく書くなら次のどちらかになりそうです。
# 末尾に「|」以降を書き加える
Select-String "pattern" filename | % { "{0}`t{1}`t{2}" -f $_.Path, $_.LineNumber, $_.Line }
Select-String "pattern" filename | % { ( $_.Path, $_.LineNumber, $_.Line) -join "`t" }
色々と試行錯誤したい
検索結果をまず先に変数に格納しておけば毎回検索する手間が省けるので話が早くなります。
$searchresult = Select-String "pattern" filename
これでまず検索結果が変数に入ります(画面には何も出ません)。
$searchresult
とだけ入力して実行すれば検索結果が出力されますし、
$searchresult | Select Line
$searchresult | % { "{0}`t{1}`t{2}" -f $_.Path, $_.LineNumber, $_.Line }
のように色々と試すこともできます。
結果をファイルに書き出す
検索結果は画面に出力するだけではなくファイルに書き出すこともできます。これについてはgrepと同じようにリダイレクトで吐き出すのが一番簡単でしょう。
Select-String "pattern" filename | % { ... } > result.txt
$searchresult | Select Line > result.txt
文字エンコーディングはUTF-16になります。これが気に食わない人は、末尾に> ファイル名と書き加えるのではなく、さらにパイプをつなげてOut-Fileコマンドレットに渡します。
Select-String "pattern" filename | % { ... } | Out-File result.txt -Encoding UTF8
$searchresult | Select Line | Out-File result.txt -Encoding UTF8
ここで出力されるファイルはBOMありUTF-8という微妙なフォーマットになるのですが、これは諦めてください(少しコードを書けば解決するのですが)。
PowerShellの基本
起動
まず、何はなくともPowerShellを立ち上げましょう。
- スタートメニューからPowerShellを探す
- エクスプローラーのアドレスバーに「powershell」と入力してエンターキーを押す
- エクスプローラーのメニューからPowerShellを起動する
なんでもいいです。
補完
dir, Select-Stringといったコマンドレットによって入力可能なオプションはあらかじめ決まっています。-まで入力してTABキーを連打すると、指定可能なオプションが列挙されます。たとえば-CaseSensitiveであれば、-cまで入力してTABキーを押せば一発で補完される、といった具合です。
また、ファイル名・ディレクトリ名も同じように補完が効きます。
大文字・小文字
コマンドレット・オプションの大文字・小文字は区別されません。Select-StringをSELECT-STRINGやselect-stringと書いても問題ありません。
\のエスケープっていらないの?
PowerShellでは\文字は特別な意味を持ちません。AやBや.と同じ普通の文字です。したがって\のエスケープは必要ありません。特に正規表現を書くときにはこれが楽です。
そのかわり、たとえば改行文字はn`、タブ文字は\t`と書くことになっています。バッククオートそのものを検索したいときはバッククオートを2つ連ねて書いてください。