Вывести строку, только если следующая строка НЕ ​​содержит конкретного совпадения

12

Я пытаюсь найти в файле журнала действия, которые не были завершены. Например, я регистрирую «Начальное действие для идентификатора 1234 ...», и в случае успеха следующая строка будет «Задание 1234 выполнено».

Я пытаюсь получить строки "Starting ...", за которыми НЕ следуют соответствующие строки "Completed".

Пример файла журнала

Starting activity for ID 1234
ID 1234 completed successfully
Starting activity for ID 3423
ID 3423 completed successfully
Starting activity for ID 9876
ID 9876 completed successfully
Starting activity for ID 99889
ID 99889 completed successfully
Starting activity for ID 10011
ID 10011 completed successfully
Starting activity for ID 33367
Starting activity for ID 936819
ID 936819 completed successfully

В этом примере я бы искал вывод:

Starting activity for ID 33367

... потому что за ним не следует "завершенная" строка.

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

Ищу быстрый и надежный grepили awkшаблон, чтобы дать результаты, которые мне нужны здесь.

PattMauler
источник
Я не думаю, что это легко с grep + awk, но не могли бы вы немного объяснить, почему вы это делаете? Вывод всех текущих действий, например, успех или не окончен?
маргаритка
@ warl0ck, я ищу "не закончено".
PattMauler

Ответы:

10

Вот awkальтернатива:

awk '
  /^Starting/ { I[$5] = $0                  }
  /^ID/       { delete I[$2]                }
  END         { for (key in I) print I[key] }
' infile

Выход:

Starting activity for ID 33367

IАссоциативный массив отслеживает то , что идентификаторы были замечены.

Тор
источник
Это работает очень хорошо, поскольку даже кажется, что оно подходит для ситуаций, когда строки журнала «Начало ...» и «Завершено ...» не являются смежными / последовательными. Спасибо @Thor!
PattMauler
Пожалуйста. Это должно работать эффективно при вводе (почти) произвольного размера, так как он хранит только идентификатор и время поиска равно O (1).
Тор
Ницца. Только одно: как я узнал из @RobertL ( unix.stackexchange.com/a/243550/135943 ), вам не нужно присваивать значение для создания элемента массива. Так что вместо I[$5] = 1, вы можете просто использовать I[$5]. (Вы не заботитесь о значении, вы просто хотите, чтобы элемент существовал , и просто называете его, и это достигается.)
Wildcard
@Wildcard: Вы правы, но после рассмотрения вопроса ОП и результатов поиска, подобного grep, более уместно запомнить всю строку и вывести ее в конце.
Тор
3
sed '$!N;/\n.*completed/d;P;D' <input

Это приведет к удалению с выхода всех линий ввода , которые не следуют линии , соответствующей строки завершено .

mikeserv
источник
2

Вот как вы можете сделать это с помощью GNU sed:

sed -r 'N; /([0-9]+)\n\w+\s+\1/d; P; D' infile
  • N читает еще одну строку в шаблонном пространстве.
  • Регулярное выражение соответствия проверяет, найдены ли идентичные идентификаторы, если это так, шаблонное пространство удаляется ( d) и цикл перезапускается.
  • Если он не совпадает, выведите первую строку в шаблонном пространстве ( P) и удалите его ( D).
Тор
источник
Я не вижу ничего расширенного здесь ... так -rчто не нужно, верно?
Луи Мэддокс,
1
@lmmx: это необходимо, потому что в противном случае необходимо перехватить группу захвата, и то же самое касается +квантификатора.
Тор
Ах хорошо! Я изменил его и мне сказали, что в этом нет необходимости, спасибо за разъяснения
Луи Мэддокс,
1

если ваша установка поддерживает pcregrep, вам пригодится опция multiline (-M).

pcregrep -M -o '\AStarting activity for ID (\d+)\n(?!ID \1)' t.z

Начало активности для ID 33367

Iruvar
источник