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

8

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

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

Типичная запись о событии будет выглядеть следующим образом и будет содержать другие тексты между ними. Таким образом, в приведенном ниже примере есть две записи о событиях : первая содержит две 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.
Фанат
источник
Они всегда начинаются с Data control raising event?
LatinSuD
@LatinSuD да, он всегда начинается с этой строки.
Компьютерщик
Я думаю, что это работа для awk, использующая переменную (и) "конечного автомата", но вы должны добавить дополнительную информацию, чтобы получить помощь в этом, например, поиск точных токенов и ожидаемый конечный результат.
Диди Кохен
@DavidKohen Запись события начинается с токена «Событие повышения контроля данных» и заканчивается на «]]» в новой строке. Я хотел бы выяснить каждое такое событие .
Компьютерщик
Как насчет них? Посчитать их количество? Распечатать их все? Пожалуйста, отредактируйте ваш вопрос и добавьте пример ожидаемого результата (желательно с разными входами образца).
Диди Кохен

Ответы:

4

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

Сохраните этот файл в 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в рабочем каталоге.

LatinSuD
источник
Вы должны увеличивать счетчик событий отдельно от заголовка события (или иметь оператор ранее), это приводит к тому, что верхний и нижний колонтитулы показывают разные числа и являются менее читаемыми.
Диди Кохен
@LatinSuD Я не знаком с awk. Так что, если вы можете добавить часть, которую мне нужно сделать для запуска вышеуказанной программы, это будет очень полезно. Для меня входной файл, скажем, A.log .
Компьютерщик
Чтобы использовать этот сценарий, просто замените inputfile.txt на имя вашего файла или, что лучше, удалите cat и pipe и введите имя файла после закрывающей одинарной кавычки.
Диди Кохен
@DavidKohen Я хотел бы сохранить этот скрипт. Так что, если я сохраню это как скажем findEvents.awk. Могу ли я выполнить это так: awk -f findEvents.awk A.log?
Компьютерщик
Вы можете, но вы должны сохранить только часть внутри одинарных кавычек в этом файле.
Диди Кохен
2

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

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 
Тердон
источник