Grepping para um bloco de texto com partes que podem ser opcionais

8

Eu tenho várias entradas que descrevem um evento em um arquivo de log muito grande, digamos A.log . Eu gostaria de fazer duas coisas com as entradas de eventos no arquivo de log:

  1. Conte o número de ocorrências de cada uma dessas entradas (esse não é um requisito obrigatório, mas seria bom ter).
  2. Extraia as entradas reais em um arquivo separado e estude-as posteriormente.

Uma entrada típica de evento teria a seguinte aparência e terá outros textos entre eles. Portanto, no exemplo abaixo, existem duas entradas de eventos , 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 ==== DataChangeEntrylinhas em uma entrada de evento pode ser variável. Também pode estar completamente ausente, o que indicaria carga útil de eventos vazios e é uma condição de erro e, definitivamente, também gostaria de capturar esse caso.

Como nesse caso a saída da entrada se estende por várias linhas, não estou indo muito longe usando o plain vanilla grep. Então, eu estou procurando aconselhamento especializado.

PS:

  1. 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 desses blocos encontrados. É bom ter a opção de contar o número de instâncias, mas não um requisito obrigatório.
  2. Se a solução para o problema estiver usando o awk, eu gostaria de salvar o arquivo awk e reutilizá-lo. Portanto, mencione as etapas para executar o script também. Eu sei regex e grep, mas não estou familiarizado com sed e / ou awk.
Nerd
fonte
Eles sempre começam Data control raising event?
LatinSuD 26/06
@LatinSuD sim, ele sempre começa com essa string.
26714 Geek
Eu acho que esse é um trabalho para o awk, usando uma variável "máquina de estado", mas você deve adicionar mais algumas informações para obter ajuda, como os tokens exatos pesquisados ​​e o que você espera que seja o resultado final.
precisa
@DavidKohen Uma entrada de evento começa com o token "Data control raise event" e termina em "]]" em uma nova linha. Gostaria de descobrir cada uma dessas instâncias de eventos .
26414 Geek
Descobrir o que eles têm? Contar a quantia deles? Imprimir todos eles? Edite sua pergunta e adicione amostra de saída esperada (de preferência com diferentes entradas de amostra).
precisa

Respostas:

4

Isso faria isso, espero. Os eventos vão para o eventsarquivo. 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

Saída de amostra:

Warning: Event 3 has no Data Entries
Total event count: 3

Você pode verificar todos os eventos juntos no arquivo chamado eventsno diretório de trabalho.

LatinSuD
fonte
Você deve incrementar o contador de eventos separadamente do cabeçalho do evento (ou solicitar o operador antes), isso faz com que o cabeçalho e o rodapé mostrem números diferentes e sejam menos legíveis.
Didi Kohen 26/06
@LatinSuD Eu não estou familiarizado com o awk. Portanto, se você puder adicionar a parte que preciso fazer para executar o programa acima, será muito útil. Para mim, o arquivo de entrada é A.log .
26714 Geek
Para usar esse script, substitua o inputfile.txt pelo nome do arquivo, ou melhor, remova o gato e o cachimbo e coloque o nome do arquivo após a aspas simples.
Didi Kohen 26/06
@DavidKohen Gostaria de salvar este script. Portanto, se eu salvar isso, diga findEvents.awk. Posso executá-lo assim awk -f findEvents.awk A.log:?
26714 Geek
Você poderia, mas salve apenas a parte entre aspas simples nesse arquivo.
precisa
2

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 em awk.
  • RS="]]"define o separador de registros (o que define uma "linha") como ]]. Isso significa que cada entrada será tratada como uma única linha por awk.
  • {print > NR".entry"}: imprime a linha atual (entrada) em um arquivo chamado [LineNumber].entry. Então, 1.entryconterá o primeiro, 2.entryo 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 ponto NR, 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ê executaria o script (assumindo que ele seja chamado foo.she esteja no 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 isso:

#!/usr/bin/env bash
date=$(date +%Y%m%d)
awk '{print > d"."NR".entry"}END{print NR" entries"}' RS="]]" d="$date" "$1"

O acima pressupõe que seu arquivo de log consiste exclusivamente em entradas "Evento". Se esse não for o caso, e você pode ter outras linhas, e essas linhas devem ser ignoradas, use isso:

 #!/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 
terdon
fonte