Я ищу в некоторых файлах журнала, где есть группы выполненных действий. В начале каждой группы есть строка, содержащая информацию о группе, а затем выводится множество подробных сведений о каждом действии со статусом PASS/FAIL, напечатанным в конце каждого отдельного теста.

То, что я хочу сделать, это найти любые действия, которые потерпели неудачу, и напечатать строку заголовка, за которой следует некоторое количество контекста до строки сбоя.

Например:

Start test group ID 12345
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 PASSED
Start test group ID 238284
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 FAILED

Вышеизложенное будет сведено в

Start test group ID 238284
verbose info
verbose info
Test 2 FAILED

Число строк перед флагом FAILED не является постоянным для каждого теста, и каждый тест также имеет различную среднюю длину, но постоянное число меня устраивает. В общем, я все равно забочусь только о последних нескольких строках.

Я чувствую, что это может быть немного сложно для grep, но я никогда не использовал awk для чего-либо и не знаю, с чего начать.

4 ответа4

3

Вот решение awk которое упрощает вещи, обрабатывая вывод в обратном порядке (требуется команда tac , которая является частью GNU coreutils):

Во-первых, скрипт awk (помещается в файл, такой как 'process.awk'). Это слишком много для однострочной игры bash .

BEGIN                           { output=0; any=0; }
/^Test .* FAILED/               { output=1; any=1; }
/^Test .* PASSED/               { output=0; }
/^Start test group/ && any == 1 { output=1; any=0; }
output == 1                     { print; }

Затем запустите этот сценарий в обратном файле журнала и сторнируйте вывод:

tac logfile | awk -f process.awk | tac

Как это работает?

Сначала мы передаем наш ввод через tac , чтобы изменить порядок строк (чтобы мы могли определить, принадлежат ли "следующие" строки тесту FAILED или PASSED, прежде чем их читать).

Сценарий работает следующим образом. Каждое действие состоит из условия, которое должно быть сопоставлено, за которым следует блок кода для выполнения, если текущая строка соответствует условию.

Первое действие - это действие BEGIN, которое всегда выполняется один раз, прежде чем мы начнем смотреть на вход. Он инициализировал два логических флага, которые управляют тем, что печатается. output будет установлен в 1, если мы хотим напечатать текущую строку, в противном случае 0. any будет установлен в 1 каждый раз, когда мы сталкиваемся с ошибочным тестом, и сбрасывается в 0 после того, как мы закончили обработку группы тестов. Оба значения начинаются с 0.

Следующее действие проверяет текущую строку, чтобы увидеть, является ли она началом неудачного теста (помните, мы обрабатываем вывод в обратном порядке). Если это так, установите оба output и any .

Следующее действие проверяет текущую строку, чтобы увидеть, является ли она началом пройденного теста. Если это так, снимите флаг output но оставляй в одиночку. (До конца тестовой группы все еще может быть неудачный тест).

Следующее действие проверяет текущую строку, чтобы увидеть, является ли она заголовком группы тестирования и установлен ли флаг any . Если это так, мы хотим напечатать заголовок (у нас был хотя бы один неудачный тест), поэтому установите output и очистите any (чтобы подготовиться к следующей группе тестов). В противном случае нам ничего не нужно делать; any уже равен 0, и output не может быть установлен в 1 если any никогда не было.

Наконец, у нас есть действие, которое не смотрит на текущую строку, а просто проверяет, установило ли какое-либо из предыдущих действий output . Если они есть, мы печатаем текущую строку (которая может быть строкой "Test FAILED", некоторой подробной информацией, которая "предшествует" строке FAILED, или заголовком группы тестов).

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

Обратите внимание, что скрипт можно сделать немного более эффективным за счет усложнения, но он должен быть достаточно быстрым.

1

Скрипт, полученный из скрипта Bgs в bash:

buffer=""; cat /your/file | while read line
do
    echo $line | grep -Eq "^Start" && start=$line && continue
    echo $line | grep -q "FAILED" && echo -e "$start$buffer\n$line" \
            && buffer="" && continue
    echo $line | grep -q "PASSED" && buffer="" || buffer="$buffer\n$line"
done

Для каждой строки "FAILED" строка "Start" вместе со всеми строками, предшествующими строке "FAILED", печатаются до последней строки "FAILED" или "PASSED" (исключая).

Пример входного файла:

Start test group ID 12345
verbose info #1
verbose info #2
Test 1 PASSED
verbose info #3
verbose info #4
Test 2 PASSED
verbose info #5
verbose info #6
Test 3 PASSED
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
verbose info #13
verbose info #14
Test 6 PASSED
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
verbose info #18
verbose info #19
verbose info #20
Test 8 PASSED

Вывод скрипта:

Start test group ID 12345
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
Start test group ID 98765
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
0

Думая об идеях ... есть несколько способов добраться сюда,

  1. Используйте AWK, чтобы поймать линии начала, а затем поймать от последней линии PASSED или FAILED и далее, пока не получите FAILED, в этот момент вы сбрасываете строку Start и последний пакет, ведущий к линии FAILED.

  2. Или отфильтруйте строки начала и контекст FAILED отдельно с помощью grep и объедините их. Для этого вам необходимо сохранить номера строк.

Попробуйте этот грубый AWK как стартер,

# script.awk
BEGIN {buffer1="";buffer2=""}
{ 
 if ($1 == "Start") 
 {
  buffer1=$0
 } 
 else 
 { 
  if ($3 == "PASSED") 
  {
   buffer2=""
  } 
  else 
  {
   buffer2=buffer2 "\n" $0; 
   if ($3 == "FAILED") 
   {
    printf "%s%s\n",buffer1,buffer2
   }
  }
 }
}

Запустите с помощью awk -f script.awk file.txt

Заметки:

  1. Это потребует корректировки, если ваши строки Start , PASSED или FAILED отличаются
    • довольно прямо, если они так же последовательны, как в вашем примере
  2. Это может также привести к пропускам зажигания, если в ваших verbose частях есть одно из трех указанных выше «ключевых слов» в «правильном» месте.
    • Если это так, вам нужно добавить в более контекстном, чтобы поймать правильные ключевые слова
  3. Это даст вам все неудачные строки раздела
    • Вы можете поиграться с буферами немного, чтобы получить меньше данных
0

Простое решение для bash (я сохранил ваш пример в foo.txt):

buff="" ; cat foo.txt| while read line; do echo $line| grep -q "^Start" && buff="" ; buff="$buff\n$line" ; echo $line | grep -q FAILED && echo -e $buff; done

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .