【追記】こちらの記事で触れたPowerShellの Select-String
については別記事でもう少し詳しく触れています。こちらもご覧ください。
tech.sanwasystem.com
こんにちは、PowerShellが好きなwakです。ログファイルの検索を行うときなど、Windowsでもgrepのような文字列検索を行いたい状況があります(昨日たくさんやりました)。そのための方法をメモしておきます。
インデックス
- findコマンドを使う(お手軽・低機能・UTF-8には非対応)
- findstrコマンドを使う(多少は複雑なことができる・UTF-8には非対応)
- PowerShellのSelect-Stringコマンドレットを使う(正規表現利用可・UTF-8対応)
- 簡単に結果をファイル出力する(簡単・制限あり)
- 頑張って結果をファイル出力する(面倒・書式制御可)
前もって書いておくと、PowerShellでテキスト検索を行い、その結果をUTF-8で出力する方法は次の通りです。
$utf8 = New-Object System.Text.UTF8Encoding($False) #BOMなしUTF-8 $result = Select-String "FATAL|ERROR" *.log -Encoding default # 検索結果を取得 [System.IO.File]::WriteAllLines("result4.txt", ($result | % { $_.get_line() }), $utf8) # 出力
なお、検索対象は以下の内容のファイル2件とします。文字コードは最初はShift-JISで用意してください。
2015-04-21 19:30:00 ERROR カリカリがないです 2015-04-21 20:00:00 DEBUG 寝顔がかわいい 2015-04-21 20:30:00 INFO 猫が起きました 2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました
2015-04-22 12:00:00 INFO 猫が寝ました 2015-04-22 12:30:00 DEBUG 寝顔がかわいい 2015-04-22 13:30:00 INFO 猫が起きました 2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
1. findコマンドを使う
MS-DOS時代からある由緒正しいコマンドです。ただし、 Shift-JISとUTF-16(BOMあり)のファイルにしか対応していません。
C:\blog>find "FATAL" cat20150422.log ---------- CAT20150422.LOG 2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
複数ファイルも受け付けます。
C:\blog>find "FATAL" *.log ---------- CAT20150421.LOG 2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました ---------- CAT20150422.LOG 2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
デイリーでローテーションしているログファイルの場合など、ファイル名は不要なケースもあるでしょう。上記の結果からさらに"FATAL"を検索すれば検索結果だけが絞り込めます。
C:\blog>find "FATAL" *.log | find "FATAL" 2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました 2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
100行ぐらい出てくるからクリップボードにコピーしたいな……という場合、コマンドの末尾に| clip
を付け加えましょう。上記の結果がそのままクリップボードにコピーされます。
C:\blog>find "FATAL" *.log | find "FATAL" | clip
結果をファイル出力する場合はリダイレクトしてください。
C:\blog>find "FATAL" *.log | find "FATAL" > result.txt
2. findstrコマンドを使う
findコマンドの後継がfindstrコマンドで、こちらは正規表現(のごくごく一部)が使えます。@ITの記事が網羅的なので読むと話が早いです。こちらもShift-JISにしか対応していません。
C:\blog>findstr "FATAL" *.log cat20150421.log:2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました cat20150422.log:2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
C:\blog>findstr /R "した\>" *.log cat20150421.log:2015-04-21 20:30:00 INFO 猫が起きました cat20150421.log:2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました cat20150422.log:2015-04-22 12:00:00 INFO 猫が寝ました cat20150422.log:2015-04-22 13:30:00 INFO 猫が起きました cat20150422.log:2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
3. PowerShellのSelect-Stringコマンドレットを使う
説明するより見た方が早いでしょう。
PS C:\blog> Select-String "FATAL|ERROR" *.log -Encoding default cat20150421.log:1:2015-04-21 19:30:00 ERROR カリカリがないです cat20150421.log:4:2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました cat20150422.log:4:2015-04-22 14:00:00 FATAL 猫がお皿をひっくり返しました
PowerShellの標準の文字エンコーディングはUTF-16(BOMあり)です。UTF-8は自動認識してくれるようですが、失敗する場合は文字コードを-Encoding
で明示的に指定してあげる必要があります。
文字コード | 引数 |
---|---|
Shift-JIS | default |
UTF-8 | utf8 |
UTF-16(BOMあり) | unicode |
3.1 簡単にファイル出力する
リダイレクトで出力するのが一番手っ取り早いのですが、文字エンコーディングはUTF-16(BOMあり)になります。
PS C:\blog> Select-String "FATAL|ERROR" *.log -Encoding default > result.txt
変更したい場合はOut-File
を使いましょう。UTF-8にしてみます。
PS C:\blog> Select-String "FATAL|ERROR" *.log -Encoding default | Out-File -FilePath result.txt -Encoding utf8 -Width 10000
最後の-Width
は1行あたりの最大長を指定するもので、これを超えた場合は強制的に改行が入ります。結果の書式はどちらもこのようになります。順にファイル名、行数、内容です。
cat20150421.log:1:2015-04-21 19:30:00 ERROR カリカリがないです cat20150421.log:4:2015-04-21 21:30:00 FATAL 猫がPCの上で粗相をしました
この方法は簡単ですが、難点がいくつかあります。
- 書式が選べない(上のようになる)
- 1行が一定の長さを超える場合は強制的に改行が入る(
-Width
で指定可能) - UTF-8で出力しても強制的にBOMが入る
3.2 頑張って綺麗にファイル出力する
Select-String
コマンドレットはMicrosoft.PowerShell.Commands.MatchInfo
オブジェクトの配列を返します。画面やファイルにテキスト表示する際はこのMatchInfo
クラスのToString()
メソッドが暗黙的に呼ばれるのですが、これを自分でテキスト変換すれば上述した難点の1つめは解決します。(ついでに書式を自由に決めることもできます)
さらにOut-File
コマンドレットに任せず、自前でファイル出力を行うことで難点の2つめ・3つめも解決しましょう。.NETのSystem.IO.File
クラスのWriteAllLines
メソッドを直接呼び出します。
これをまとめると冒頭のコードになります。頑張ればワンライナーになりますが……
$utf8 = New-Object System.Text.UTF8Encoding($False) #BOMなしUTF-8 $result = Select-String "FATAL|ERROR" *.log -Encoding default # 検索結果を取得 [System.IO.File]::WriteAllLines("result.txt", ($result | % { $_.get_line() }), $utf8) # 出力
PowerShellを使ってみよう
今回はテキスト検索をネタにしてみました。他環境と比べると記述量は若干増えるので、取っつきづらい面は確かにあります。しかし上でちょっと触れてもらったように、コマンドレットとコマンドレットの間は.NETのオブジェクトでやり取りされるため、柔軟性や自由度は比較になりません。
さして量が多くなければメモ帳で開いて検索することもできますが、それを繰り返すのは時間の無駄ですし、ミスが紛れ込む危険も生じます。PowerShellの利点はWindowsでありさえすれば利用できることがほぼ間違いなく保証されていることで、今後もWindowsでお仕事をするあなたの頼もしい相棒になるでしょう。