Grepping для блока текста с частями, которые могут быть необязательными

У меня есть несколько записей, которые описывают событие в очень большом файле журнала, например A.log . Я хотел бы сделать две вещи с записями событий в файле журнала:

  1. Подсчитайте количество вхождений каждой такой записи (это не обязательное требование, но было бы неплохо иметь).
  2. Извлеките фактические записи в отдельный файл и изучите их позже.

Типичная запись события будет выглядеть следующим образом и будет иметь между собой другие тексты. Итак, в приведенном ниже примере есть две записи событий , первая из которых содержит две DataChangeEntry и вторую, содержащую одну DataChangeEntry нагрузку 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, ] ]] 

Обратите внимание, что число строк ==== DataChangeEntry в записи события может быть переменной. Он также может быть полностью отсутствующим, что указывает на полезную нагрузку на пустые события и является условием ошибки, и определенно хотелось бы поймать этот случай.

Так как в этом случае вывод ввода охватывает несколько строк, я не получаю далеко, используя простой ванильный grep. Поэтому я ищу экспертную консультацию.

PS:

  1. Позвольте мне более подробно рассказать о моем требовании. Я хотел бы захватить весь блок текста, показанный выше, и, при необходимости, подсчитать количество экземпляров таких блоков. Возможность подсчета количества экземпляров хороша, но не является обязательным требованием.
  2. Если решение проблемы использует awk, я хотел бы сохранить файл awk и повторно использовать его. Поэтому, пожалуйста, укажите шаги для выполнения сценария. Я знаю регулярное выражение и grep, но я не знаком с sed и / или awk.

Надеюсь, это будет так. События идут в файл events . И сообщения идут в stdout.

Сохраните этот файл в myprogram.awk (например):

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

Вы можете вызвать его по-разному:

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

Пример вывода:

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

Вы можете проверить все события вместе в файле, называемом events в рабочем каталоге.

Очень простой подход

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

Это создаст отдельный файл для каждой записи и напечатает количество записей, найденных для стандартного вывода.

объяснение

  • NR – номер текущей строки в awk .
  • RS="]]" устанавливает разделитель записи (что определяет «линию») на ]] . Это означает, что каждая запись будет обрабатываться как одна строка awk .
  • {print > NR".entry"} : это печатает текущую строку (запись) в файл с именем [LineNumber].entry . Итак, 1.entry будет содержать 1-й, 2.entry -второй второй и так далее.
  • END{print NR" entries"} : блок END выполняется после обработки всего входного файла. Поэтому в этой точке NR будет количество обработанных записей.

Вы можете сохранить это как псевдоним или сделать его сценарием следующим образом:

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

Затем вы запустили скрипт (предположив, что он называется foo.sh и находится в вашем PATH) с целевым файлом в качестве аргумента:

 foo.sh file 

Вы также можете настроить имена выходных файлов. Например, чтобы файлы назывались [date].[entry number].[entry] используйте это вместо:

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

Вышеизложенное предполагает, что ваш файл журнала состоит исключительно из записей «Событие». Если это не так, и вы можете иметь другие строки, и эти строки следует игнорировать, используйте вместо этого:

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

Или, как однострочный:

 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