くまくまの業務日誌

Markdown記法で徒然に書いてみましょう。

ログからキーワードを抽出して、ソートして、同じものを切り捨てる 壱

コマンドプロンプトでは、よくやるコマンドのテンプレート

ログファイルに対してよくやることは、たぶん以下の作業

  • 履歴をファイルをまとめて1つに集約
  • ファイルの中身を出力
  • ファイルの中身からキーワードを含む行を抽出
  • 抽出した内容をソート
    ログの場合、タイムスタンプがあるし、そもそも昇順
    しかし、タイムスタンプ部をスキップして、メッセージだけをソートしたいことはある
  • ソートした中身から重複するものを切り捨てる
    Windowsには該当コマンドなし
    Unix系にはuniqコマンドがある(要ソート)

いい加減、MS-DOSから脱却したいのにいつまでも、この操作をMS-DOSでやっているので、まじめにPowerShellスクリプトを考えようと思う。

複数のファイルを連結して、1つのファイルに集約

コマンドプロンプトの場合

最初の元凶がここ。スマートな方法があるはず。今まで10個の履歴ファイルを一生懸命書いて連結していたけど、もう飽きた。

copy [ファイル1] + [ファイル2] + [ファイル3] [集約するファイル名]

シェル系

gitをインストールすると、WindowsでもUnixコマンドが使えるので便利だし、任意のフォルダからエクスプローラの右クリックでBash起こせるから、こっちを覚えるべきかもしれないけど、それはまたの機会に。

# 連結するファイル名にワイルドカードを使うと一気に連結
# 順番はきっとASCII順 履歴番号ありとなしで順番が意図しない形になるかもしれない
# Output.log.*
# 順番が重要であるなら、列挙して最後にリダイレクトする。
# 最後にリダイレクトでファイル出力するのを忘れずに

cat [連結するファイル名] > [集約するファイル名]
cat [ファイル1] [ファイル2] [ファイル3] > [集約するファイル名]

PowerShell

まずはネットで、やり方を調べてみると以下の3つが上がってきた。*-Contentで出力するのが正統派に思えてきたけど、リダイレクトもパイプラインとしては王道なので、テンプレート検討としては悩むところ。

Get-Contentで渡すワイルドカードでは、"*.log.*"を指定した場合、"hogehoge.log"は拾ってくれなかったのは困りもの。

Get-ChildItem -File -Filter ["ワイルドカード"] | Get-Content | Add-Content [集約するファイル名]
Get-Content [ワイルドカードで指定するファイル名] > [集約するファイル名]
Get-Content [ファイル名1], [ファイル名2], [ファイル名3] | Set-Content [集約するファイル名]

ちなみに私の職場のログファイルは…
hogehoge.log
hogehoge.log.01
hogehoge.log.02
hogehoge.log.03
となっていて、ちと扱いが辛い。逆順にする必要もある。しかし、これは以下のやり方で解決できた。
これで、ファイル名を逆順に出力させて1つのファイルに集約できる。後はリダイレクトか、*-Contentで出力する。

Get-ChildItem -File -Filter "*.log.*" | Sort-Object -Descending
PS C:\Users\USER\Desktop\work> Get-ChildItem -file -filter "*.log.*" | Sort-Object -Descending


    ディレクトリ: C:\Users\USER\Desktop\work


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/09/12     16:38             16 output.log.03
-a----       2020/09/12     16:37             16 output.log.02
-a----       2020/09/12     16:37             16 output.log.01
-a----       2020/09/12     16:37             16 output.log

コマンドプロンプトで最初に叩く、ログの集約作業はこれで行こう。

Get-ChildItem -File -Filter "*.log.*" | Sort-Object -Descending | Get-Content > [出力ファイル名]

必要なキーワードで抽出する。

MS-DOS

find.exeは昔からの人がつい、手なりで叩いてしまいますが、今は正規表現も使えるfindstr.exeが本流なのでしょう。正規表現のリファレンスはここ(@IT)でも良いかと。

type [入力ファイル名] | find "キーワード名"
type [入力ファイル名] | findstr "キーワード名"

find "キーワード名" < [入力ファイル名]
findstr "キーワード名" < [入力ファイル名]

シェル系

grepは検索の王道なので説明するまでもなく…

cat [入力ファイル名] | grep "キーワード名"
grep "キーワード名" < [入力ファイル名]

PowerShell

正規表現も利用したいですし、Select-Stringを使いましょう。

Get-Content -Path <入力ファイル名> | Select-String -Pattern '正規表現'

ここで、単に ERROR の文字を抽出するのであれば、

Get-Content [入力ファイル名] | Where-Object { $_ -like "*ERROR*" }
Get-Content [入力ファイル名] | Where-Object { $_ -match "ERROR" }

も、ありかもしれません。しかし、シンプルなコードにするなら、Select-Stringがよいと思われます。

データのソートに関しては、ログに関しては桁位置がガタガタ(スレッド名の設定を%tにしてしまったので3~5桁でそろわない)なので、まずここをやっつけなければなりませんが、このネタだけで長くなりそうです。

ということで、次回の講釈で。