Script/regex: extração em várias linhas
Pergunta
Estou tentando escrever um script de análise de log para extrair eventos com falha. Eu posso puxá -las com 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
O que estou interessado é, para cada bloco, o código de erro (ou seja, o "023" parte de ": falha err: 023" na primeira linha) e o número do DST (ou seja, "447872123456" de "dst = 447872123456" na última linha.)
Alguém pode ajudar com uma lineadora para extrair esses dois valores ou fornecer algumas dicas sobre como devo abordar isso?
Solução
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
Como em todos os lineadores de "um" da Shell, há quase certamente uma maneira mais elegante de fazer isso. No entanto, acho a abordagem iterativa muito bem -sucedida para obter o que quero: comece com muita informação (seu grep), depois restrinja as linhas que eu quero (com grep) e depois retire as partes de cada linha que eu quero (com com cortar).
Enquanto o uso da caixa de ferramentas Linux leva mais linhas, você só precisa conhecer o básico de alguns comandos para fazer o que quiser. Uma alternativa é usar idiomas AWK, Python ou de script, que requerem conhecimento de programação mais especializado, mas terá menos espaço na tela.
Outras dicas
Uma solução simples em Ruby, aqui está 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
Execute -o com:
ruby filter.rb my_log_file.txt
E você recebe:
023 447872123456
024 447872987654
Se sempre houver o mesmo número de campos, você poderia apenas
grep -A5 "FAILED" log.txt | awk '$24~/err/ {print $24} $12~/dst/{print $12}' error.txt
err:023
dst=447872123456,
err:024
dst=447872987654,
E, dependendo da aparência do restante do arquivo, você poderá pular o Grep All Togther.
O "$ 24 ~/ err/ {print $ 24}"A parte diz ao AWK para imprimir o número 24 do campo se contiver err, ~/ xxx/ onde xxx é uma expressão regular.