Grepping para un bloque de texto con partes que pueden ser opcionales

8

Tengo varias entradas que describen un evento en un archivo de registro muy grande, digamos A.log . Me gustaría hacer dos cosas con las entradas de eventos en el archivo de registro:

  1. Cuente el número de ocurrencias de cada una de estas entradas (este no es un requisito obligatorio, pero sería bueno tenerlo).
  2. Extraiga las entradas reales en un archivo separado y estudielas más adelante.

Una entrada de evento típica sería similar a la siguiente y tendrá otros textos entre ellas. Entonces, en el ejemplo a continuación hay dos entradas de eventos , la primera que contiene dos DataChangeEntry cargas útiles y la segunda que contiene una 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,  ]

    ]]

Tenga en cuenta que el número de ==== DataChangeEntrylíneas en una entrada de evento puede ser variable. También puede estar completamente ausente, lo que indicaría una carga útil de eventos vacía y es una condición de error y definitivamente también me gustaría detectar este caso.

Como en este caso la salida de la entrada se extiende a través de varias líneas, no estoy llegando lejos usando grep simple de vainilla. Por eso estoy buscando consejos de expertos.

PD:

  1. Déjame ser más explícito sobre mi requerimiento. Me gustaría capturar todo el bloque de texto que se muestra arriba textualmente y, opcionalmente, contar el número de instancias de dichos bloques encontrados. Es bueno tener la opción de contar el número de instancias, pero no es un requisito obligatorio.
  2. Si la solución al problema es usar awk, me gustaría guardar el archivo awk y volver a usarlo. Por lo tanto, mencione los pasos para ejecutar el script también. Sé regex y grep pero no estoy familiarizado con sed y / o awk.
Friki
fuente
¿Siempre comienzan con Data control raising event?
LatinSuD
@LatinSuD sí, siempre comienza con esa cadena.
Geek
Creo que este es un trabajo para awk, usando una (s) variable (s) de "máquina de estado", pero debe agregar más información para obtener ayuda con esto, como los tokens exactos buscados y cuál espera que sea el resultado final.
Didi Kohen
@DavidKohen Una entrada de evento comienza con el token "Evento de aumento de control de datos" y termina en "]]" en una nueva línea. Me gustaría conocer cada una de esas instancias de eventos .
Geek
¿Qué hay de ellos? Cuenta su cantidad? Imprimirlos todos? Edite su pregunta y agregue una salida esperada de muestra (preferiblemente con diferentes entradas de muestra).
Didi Kohen

Respuestas:

4

Esto lo haría, espero. Los eventos van a eventsarchivo. Y los mensajes van a stdout.

Guarde este archivo en myprogram.awk (por ejemplo):

#!/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
}

Puede invocarlo de diferentes maneras:

  • myprogram.awk inputfile.txt
  • awk -f myprogram.awk inputfile.txt

Salida de muestra:

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

Puede verificar todos los eventos juntos en el archivo llamado eventsen el directorio de trabajo.

LatinSuD
fuente
Debe incrementar el contador de eventos por separado del encabezado del evento (o tener el operador antes), esto hace que el encabezado y el pie de página muestren números diferentes y sea menos legible.
Didi Kohen
@LatinSuD No estoy familiarizado con awk. Entonces, si puede agregar la parte que necesito hacer para ejecutar el programa anterior, será muy útil. Para mí, el archivo de entrada es A.log .
Geek
Para usar este script simplemente reemplace inputfile.txt con su nombre de archivo, o mejor, elimine el gato y la tubería, y coloque su nombre de archivo después de la comilla simple de cierre.
Didi Kohen
@DavidKohen Me gustaría guardar este script. Entonces, si guardo esto como digamos findEvents.awk. ¿Puedo ejecutarlo así awk -f findEvents.awk A.log:?
Geek
Podría, pero debe guardar solo la parte dentro de las comillas simples en ese archivo.
Didi Kohen
2

Un enfoque muy simple sería

awk '{print > NR".entry"}END{print NR" entries"}' RS="]]" file 

Esto creará un archivo separado para cada entrada e imprimirá el número de entradas encontradas en la salida estándar.

Explicación

  • NRes el número de línea actual en awk.
  • RS="]]"establece el separador de registros (lo que define una "línea") en ]]. Esto significa que cada entrada será tratada como una sola línea por awk.
  • {print > NR".entry"}: esto imprime la línea actual (entrada) en un archivo llamado [LineNumber].entry. Por lo tanto, 1.entrycontendrá el primero, 2.entryel segundo y así sucesivamente.
  • END{print NR" entries"}: el bloque END se ejecuta después de que se haya procesado todo el archivo de entrada. Por lo tanto, en ese punto NRserá el número de entradas procesadas.

Puede guardar esto como un alias o convertirlo en un script de esta manera:

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

Luego ejecutaría el script (suponiendo que se llama foo.shy está en su $ PATH) con el archivo de destino como argumento:

foo.sh file

También puede modificar los nombres de los archivos de salida. Por ejemplo, para llamar los archivos, [date].[entry number].[entry]use esto en su lugar:

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

Lo anterior supone que su archivo de registro consta exclusivamente de entradas de "Evento". Si ese no es el caso, y puede tener otras líneas, y esas líneas deben ignorarse, use esto en su lugar:

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

O, como una frase:

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
fuente