
Tenho várias entradas que descrevem umeventoem um arquivo de log muito grande, digamosUm registo. Eu gostaria de fazer duas coisas com oeventoentradas no arquivo de log:
- Conte o número de ocorrências de cada entrada. (Este não é um requisito obrigatório, mas seria bom ter.)
- Extraia as entradas reais em um arquivo separado e estude-as mais tarde.
Uma entrada de evento típica teria a seguinte aparência e teria outros textos entre elas. Portanto, no exemplo abaixo, existem doiseventoentradas, a primeira contendo duas DataChangeEntry
cargas úteis e a segunda contendo uma DataChangeEntry
carga útil.
Data control raising event :DataControl@263c015d[[
#### DataChangeEvent #### on [DataControl name=PatternMatch_LegendTimeAxis, binding=.dynamicRegion1. beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxisPageDef_beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxis_xml_ps_taskflowid.dynamicRegion58. beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxisPageDef_beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxis_xml_ps_taskflowid.QueryIterator]
Filter/Collection Id : 0
Collection Level : 0
Sequence Id : 616
ViewSetId : PatternMatch.LegendTimeAxis_V1_0_SN49
==== DataChangeEntry (#1)
ChangeType : UPDATE
KeyPath : [2014-06-26 06:15:00.0, 0]
AttributeNames : [DATAOBJECT_CREATED, COUNTX, QueryName]
AttributeValues : [2014-06-26 06:15:00.0, 11, StrAvgCallWaitTimeGreaterThanThreshold]
AttributeTypes : [java.sql.Timestamp, java.lang.Integer, java.lang.String, ]
==== DataChangeEntry (#2)
ChangeType : UPDATE
KeyPath : [2014-06-26 06:15:00.0, 0]
AttributeNames : [DATAOBJECT_CREATED, COUNTX, QueryName]
AttributeValues : [2014-06-26 06:15:00.0, 9, AverageCallWaitingTimeGreateThanThreshold]
AttributeTypes : [java.sql.Timestamp, java.lang.Integer, java.lang.String, ]
]]
someother non useful text
spanning multiple lines
Data control raising event :DataControl@263c015d[[
#### DataChangeEvent #### on [DataControl name=PatternMatch_LegendTimeAxis, binding=.dynamicRegion1. beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxisPageDef_beam_project_PatternMatch_dashboard_LegendTimeAxis_taskflow_LegendTimeAxis_beamDashboardLegendTimeAxis_xml_ps_taskflowid.dynamicRegion58. beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxisPageDef_beam_project_PatternMatch_view_LegendTimeAxis_taskflow_LegendTimeAxis_beamVizLegendTimeAxis_xml_ps_taskflowid.QueryIterator]
Filter/Collection Id : 0
Collection Level : 0
Sequence Id : 616
ViewSetId : PatternMatch.LegendTimeAxis_V1_0_SN49
==== DataChangeEntry (#1)
ChangeType : UPDATE
KeyPath : [2014-06-26 06:15:00.0, 0]
AttributeNames : [DATAOBJECT_CREATED, COUNTX, QueryName]
AttributeValues : [2014-06-26 06:15:00.0, 11, StrAvgCallWaitTimeGreaterThanThreshold]
AttributeTypes : [java.sql.Timestamp, java.lang.Integer, java.lang.String, ]
]]
Observe que o número de ==== DataChangeEntry
linhas em uma entrada de evento pode ser variável. Também pode estar completamente ausente, o que indicaria carga útil de eventos vazia e é uma condição de erro e definitivamente gostaria de capturar esse caso também.
Como neste caso a saída da entrada se estende por várias linhas, não estou indo muito longe usando o grep simples. Portanto, estou procurando aconselhamento especializado.
PS:
- Deixe-me ser mais explícito sobre minha exigência. Gostaria de capturar todo o bloco de texto mostrado acima literalmente e, opcionalmente, contar o número de instâncias de tais blocos encontradas. A opção de contar o número de instâncias é boa, mas não é um requisito obrigatório.
- Se a solução para o problema for usar o awk, gostaria de salvar o arquivo awk e reutilizá-lo. Portanto, mencione também as etapas para executar o script. Conheço regex e grep, mas não estou familiarizado com sed e/ou awk.
Responder1
Isso resolveria, espero. Os eventos vão para events
arquivo. E as mensagens vão para stdout.
Salve este arquivo em myprogram.awk (por exemplo):
#!/usr/bin/awk -f
BEGIN {
s=0; ### state. Active when parsing inside an event
nevent=0; ### Current event number
printf "" > "events"
}
# Start of event
/^ *Data control raising event/ {
s=1;
dentries=0;
print "*** Event number: " nevent >> "events"
nevent++
}
# Standard event line
s==1 {
print >> "events"
}
# DataChangeEntry line
/^ *==== DataChangeEntry/ {
dentries ++
}
# End of event
s==1 && /^ *\]\]/ {
s=0;
print "" >> "events"
if(dentries==0){
print "Warning: Event " nevent " has no Data Entries"
}
}
END {
print "Total event count: " nevent
}
Você pode invocá-lo de diferentes maneiras:
myprogram.awk inputfile.txt
awk -f myprogram.awk inputfile.txt
Exemplo de saída:
Warning: Event 3 has no Data Entries
Total event count: 3
Você pode verificar todos os eventos juntos no arquivo chamado events
no diretório de trabalho.
Responder2
Uma abordagem muito simples seria
awk '{print > NR".entry"}END{print NR" entries"}' RS="]]" file
Isso criará um arquivo separado para cada entrada e imprimirá o número de entradas encontradas na saída padrão.
Explicação
NR
é o número da linha atual emawk
.RS="]]"
define o separador de registros (o que define uma "linha") como]]
. Isso significa que cada entrada será tratada como uma única linha porawk
.{print > NR".entry"}
: imprime a linha atual (entrada) em um arquivo chamado[LineNumber].entry
. Então,1.entry
conterá o 1º,2.entry
o segundo e assim por diante.END{print NR" entries"}
: o bloco END é executado após todo o arquivo de entrada ter sido processado. Portanto, nesse pontoNR
será o número de entradas processadas.
Você pode salvar isso como um alias ou transformá-lo em um script como este:
#!/usr/bin/env bash
awk '{print > NR".entry"}END{print NR" entries"}' RS="]]" "$1"
Você então executaria o script (supondo que ele seja chamado foo.sh
e esteja em seu $PATH) com o arquivo de destino como argumento:
foo.sh file
Você também pode ajustar os nomes dos arquivos de saída. Por exemplo, para que os arquivos sejam chamados, [date].[entry number].[entry]
use isto:
#!/usr/bin/env bash
date=$(date +%Y%m%d)
awk '{print > d"."NR".entry"}END{print NR" entries"}' RS="]]" d="$date" "$1"
O texto acima pressupõe que seu arquivo de log consiste exclusivamente em entradas de "Eventos". Se esse não for o caso, e você puder ter outras linhas, e essas linhas devem ser ignoradas, use isto:
#!/usr/bin/env bash
date=$(date +%Y%m%d)
awk '{
if(/\[\[/){a=1; c++;}
if(/\]\]/){a=0; print > d"."c".entry"}
if(a==1){print >> d"."c".entry"}
}' d="$date" file
Ou, como uma linha:
awk '{if(/\[\[/){a=1; c++;}if(/\]\]/){a=0; print > d"."c".entry"}if(a==1){print >> d"."c".entry"}}' d=$(date +%Y%m%d) file