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 evento no arquivo de log:

  1. Conte o número de ocorrências de cada entrada (este 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 de evento típica seria semelhante à seguinte e terá outros textos entre eles. Portanto, no exemplo abaixo, há duas entradas evento , a primeira contendo duas% de carga útilDataChangeEntry e a segunda contendo uma carga útil DataChangeEntry .

    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,  ]

    ]]

Por favor, note que o número de ==== DataChangeEntry linhas em uma entrada de evento pode ser variável. Também pode ser completamente ausente, o que indicaria carga útil de eventos vazios e é uma condição de erro e definitivamente gostaria de capturar este caso também.

Como neste caso a saída da entrada se estende por várias linhas, não estou conseguindo usar o grep baunilha comum. Então estou procurando conselhos de especialistas.

P.S:

  1. Deixe-me ser mais explícito sobre minha exigência. Eu gostaria de capturar todo o bloco de texto mostrado acima textualmente e, opcionalmente, contar o número de instâncias de tais blocos encontrados. A opção de contar o número de instâncias é boa, mas não é obrigatória.
  2. Se a solução para o problema está usando o awk, eu gostaria de salvar o arquivo awk e reutilizá-lo. Então, por favor mencione os passos para executar o script também. Eu sei regex e grep mas não estou familiarizado com sed e / ou awk.
por Geek 26.06.2014 / 18:16

2 respostas

4

Isso faria isso, espero. Eventos vão para o arquivo events . E as mensagens vão para o 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 maneiras diferentes:

  • 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.

    
por 26.06.2014 / 18:50
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 registro (o que define uma "linha") como ]] . Isso significa que cada entrada será tratada como uma única linha por awk .
  • {print > NR".entry"} : isto imprime a linha atual (entrada) em um arquivo chamado [LineNumber].entry . Portanto, 1.entry conterá o primeiro, 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 ponto NR será o número de entradas processadas.

Você pode salvá-lo como um alias ou transformá-lo em um script assim:

#!/usr/bin/env bash
awk '{print > NR".entry"}END{print NR" entries"}' RS="]]" "$1"

Você então executaria o script (assumindo que ele é chamado de foo.sh e está em seu $ PATH) com o arquivo de destino como um argumento:

foo.sh file

Você também pode ajustar os nomes dos arquivos de saída. Por exemplo, para que os arquivos sejam chamados de [date].[entry number].[entry] , use:

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

O acima assume que o seu arquivo de log consiste exclusivamente de entradas "Event". Se não for esse 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 um verso:

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 
    
por 26.06.2014 / 19:53