Grepping per un blocco di testo con parti che possono essere opzionali


8

Ho più voci che descrivono un evento in un file di registro molto grande, ad esempio A.log . Vorrei fare due cose con le voci dell'evento nel file di registro:

  1. Conta il numero di occorrenze di ciascuna di queste voci (questo non è un requisito obbligatorio ma sarebbe bello avere).
  2. Estrarre le voci effettive in un file separato e studiarle in seguito.

Una voce tipica dell'evento sarebbe simile alla seguente e conterrà altri testi. Quindi nell'esempio seguente ci sono due voci di evento , la prima contenente due DataChangeEntry payload e la seconda contenente un DataChangeEntry payload.

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

    ]]

Si noti che il numero di ==== DataChangeEntryrighe in una voce di evento può essere variabile. Può anche essere completamente assente, il che indica un payload di eventi vuoti ed è una condizione di errore e vorrebbe sicuramente prendere anche questo caso.

Dato che in questo caso l'output di entry si estende su più righe, non sto andando molto lontano usando plain vanilla grep. Quindi cerco il consiglio di un esperto.

PS:

  1. Vorrei essere più esplicito sul mio requisito. Vorrei catturare l'intero blocco di testo mostrato sopra alla lettera e facoltativamente contare il numero di istanze di tali blocchi riscontrati. L'opzione per contare il numero di istanze è buona ma non è obbligatoria.
  2. Se la soluzione al problema utilizza awk, vorrei salvare il file awk e riutilizzarlo. Quindi, per favore, menziona i passaggi per eseguire anche lo script. Conosco regex e grep ma non ho familiarità con sed e / o awk.

Cominciano sempre con Data control raising event?
LatinSuD,

@LatinSuD sì, inizia sempre con quella stringa.
Geek,

Penso che questo sia un lavoro per awk, usando una o più variabili "machine a stati", ma dovresti aggiungere qualche informazione in più per ottenere aiuto in questo, come i token esatti cercati e quello che ti aspetti sia il risultato finale.
Didi Kohen,

@DavidKohen Una voce di evento inizia con il token "Evento di controllo controllo dati" e termina in "]]" in una nuova riga. Vorrei scoprire ciascuna di queste istanze di eventi .
Geek,

Cosa ne pensi di loro? Conta il loro importo? Stampali tutti? Modifica la tua domanda e aggiungi l'output atteso del campione (preferibilmente con input campione diversi).
Didi Kohen,

Risposte:


4

Lo farei spero. Gli eventi vanno al eventsfile. E i messaggi vanno a stdout.

Salvare questo file su myprogram.awk (ad esempio):

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

Puoi invocarlo in diversi modi:

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

Uscita campione:

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

Puoi controllare tutti gli eventi insieme nel file chiamato eventsnella directory di lavoro.


È necessario incrementare il contatore eventi separatamente dall'intestazione dell'evento (o avere l'operatore prima), in questo modo l'intestazione e il piè di pagina mostrano numeri diversi ed è meno leggibile.
Didi Kohen,

@LatinSuD Non ho familiarità con Awk. Quindi, se puoi aggiungere la parte che devo fare per eseguire il programma sopra, sarà molto utile. Per me il file di input è dire A.log .
Geek,

Per utilizzare questo script è sufficiente sostituire inputfile.txt con il nome del file, o meglio, rimuovere cat and pipe e inserire il nome del file dopo la virgoletta singola di chiusura.
Didi Kohen,

@DavidKohen Vorrei salvare questo script. Quindi, se lo salvo come dire findEvents.awk. Posso eseguirlo in questo modo awk -f findEvents.awk A.log:?
Geek,

Potresti, ma dovresti salvare solo la parte tra virgolette singole in quel file.
Didi Kohen,

2

Un approccio molto semplice sarebbe

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

Ciò creerà un file separato per ciascuna voce e stamperà il numero di voci trovate nell'output standard.

Spiegazione

  • NRè il numero di riga corrente in awk.
  • RS="]]"imposta il separatore di record (ciò che definisce una "linea") su ]]. Ciò significa che ogni voce verrà trattata come una riga singola da awk.
  • {print > NR".entry"}: stampa la riga corrente (voce) in un file chiamato [LineNumber].entry. Quindi, 1.entryconterrà il primo, 2.entryil secondo e così via.
  • END{print NR" entries"}: il blocco END viene eseguito dopo l'elaborazione dell'intero file di input. Pertanto, a quel punto NRsarà il numero di voci elaborate.

Puoi salvarlo come alias o trasformarlo in uno script in questo modo:

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

Dovresti quindi eseguire lo script (supponendo che sia chiamato foo.shed è nel tuo $ PATH) con il file di destinazione come argomento:

foo.sh file

Puoi anche modificare i nomi dei file di output. Ad esempio, per chiamare i file, [date].[entry number].[entry]utilizzare questo:

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

Quanto sopra presuppone che il file di registro sia costituito esclusivamente da voci "Evento". In caso contrario, è possibile disporre di altre righe e tali righe devono essere ignorate, utilizzare invece questa:

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

Oppure, come one-liner:

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 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.