こんにちは、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つ連ねて書いてください。