シェルスクリプト/正規表現:複数行にわたる抽出
質問
ログ解析スクリプトを記述して、失敗したイベントを抽出しようとしています。これらをgrepで取得できます:
$ grep -A5 "FAILED" log.txt
2008-08-19 17:50:07 [7052] [14] DEBUG: data: 3a 46 41 49 4c 45 44 20 20 65 72 72 3a 30 32 33 :FAILED err:023
2008-08-19 17:50:07 [7052] [14] DEBUG: data: 20 74 65 78 74 3a 20 00 text: .
2008-08-19 17:50:07 [7052] [14] DEBUG: Octet string dump ends.
2008-08-19 17:50:07 [7052] [14] DEBUG: SMPP PDU dump ends.
2008-08-19 17:50:07 [7052] [14] DEBUG: SMPP[test] handle_pdu, got DLR
2008-08-19 17:50:07 [7052] [14] DEBUG: DLR[internal]: Looking for DLR smsc=test, ts=1158667543, dst=447872123456, type=2
--
2008-08-19 17:50:07 [7052] [8] DEBUG: data: 3a 46 41 49 4c 45 44 20 20 65 72 72 3a 30 32 34 :FAILED err:024
2008-08-19 17:50:07 [7052] [8] DEBUG: data: 20 74 65 78 74 3a 20 00 text: .
2008-08-19 17:50:07 [7052] [8] DEBUG: Octet string dump ends.
2008-08-19 17:50:07 [7052] [8] DEBUG: SMPP PDU dump ends.
2008-08-19 17:50:07 [7052] [8] DEBUG: SMPP[test] handle_pdu, got DLR
2008-08-19 17:50:07 [7052] [8] DEBUG: DLR[internal]: Looking for DLR smsc=test, ts=1040097716, dst=447872987654, type=2
私が興味を持っているのは、各ブロックのエラーコード(つまり、1行目の「:FAILED err:023」の「023」部分)とdst番号(「447872123456」)です。最後の行の" dst = 447872123456"から)。
これらの2つの値を抽出するために、シェルワンライナーを手伝ってくれる人はいますか?
解決
grep -A 5 FAILED log.txt | \ # Get FAILED and dst and other lines
egrep '(FAILED|dst=)' | \ # Just the FAILED/dst lines
egrep -o "err:[0-9]*|dst=[0-9]*" | \ # Just the err: and dst= phrases
cut -d':' -f 2 | \ # Strip "err:" from err: lines
cut -d '=' -f 2 | \ # Strip "dst=" from dst= lines
xargs -n 2 # Combine pairs of numbers
023 447872123456
024 447872987654
すべてのシェル「1」ライナーと同様に、これを行うにはもっと確実な方法がほぼ確実にあります。しかし、私は欲しいものを得るための反復アプローチが非常に成功していることを発見します:多すぎる情報(あなたのgrep)で始めてから、(grepで)必要な行を絞り込んでから、必要な各行の部分を切り取ります(カット)。
Linuxツールボックスを使用するとより多くの行が必要になりますが、必要なことを行うには、いくつかのコマンドの基本を知っていれば十分です。別の方法は、awk、python、またはその他のスクリプト言語を使用することです。これらの言語は、より専門的なプログラミングの知識を必要としますが、必要な画面スペースは少なくなります。
他のヒント
Rubyのシンプルなソリューションは、 filter.rb
です:
#! /usr/bin/env ruby
File.read(ARGV.first).scan(/:FAILED\s+err:(\d+).*?, dst=(\d+),/m).each do |err, dst|
puts "#{err} #{dst}"
end
次で実行:
ruby filter.rb my_log_file.txt
次のようになります:
023 447872123456
024 447872987654
常に同じ数のフィールドがある場合は、ただ
grep -A5 "FAILED" log.txt | awk '$24~/err/ {print $24} $12~/dst/{print $12}' error.txt
err:023
dst=447872123456,
err:024
dst=447872987654,
そして、ファイルの残りの部分の見え方によっては、grepをすべてスキップできます。
" $ 24〜/ err / {print $ 24} " partは、errに〜/ XXX /が含まれている場合、フィールド番号24を出力するようにawkに指示します。XXXは正規表現です。