
Доброе утро, это очень похоже на вопросGrep от последнего вхождения шаблона до другого шаблона(несколько месяцев назад), добавив немного больше деталей.
Я пытаюсь написать скрипт UNIX для файла с несколькими дубликатами шаблонов, за которым следует шаблон, который я ищу. Однако у меня нет 'tac' или 'tail -r' (используя эмулятор UNIX, MKS Toolkit), и я хочу вернуть последнее вхождение Pattern1 перед Pattern2, за которым следуют данные между Pattern1 и Pattern2, а затем также Pattern2. Шаблонами в этом случае будут 'Condition 1' и 'Condition 2':
выход.выход:
...
Condition 1: A
data1
Condition 1: B
data2
Condition 2: C
data3
Condition 1: D
data4
Condition 1: E
data5
Condition 2: F
...
Я хотел бы написать скрипт на awk (или sed, но решил, что awk будет правильным инструментом) для возврата:
Condition 1: B
data2
Condition 2: C
Condition 1: E
data5
Condition 2: F
Я думаю, что это какая-то форма строки ниже, но я не могу понять синтаксис правильно:
awk '/Condition 1/ {acc = $0;} /,/Condition 2/ {print ?}' output.out
Работа с '/,/', похоже, вызывает у меня зависания. Хотелось бы узнать, есть ли у кого-нибудь совет, буду очень признателен. Большое спасибо за любую помощь и время, потраченное на этот вопрос.
решение1
Пытаться:
$ awk 'f{a=a"\n"$0} /Condition 1/{a=$0; f=1} f && /Condition 2/{print a; f=0}' output.out
Condition 1: B
data2
Condition 2: C
Condition 1: E
data5
Condition 2: F
Как это работает
f{a=a"\n"$0}
Если переменная
f
истинна (не равна нулю), то добавить текущую строку в конец переменнойa
./Condition 1/{a=$0; f=1}
Если текущая строка содержит
Condition 1
, то установитеs
текущую строку и установите переменнуюf
в 1.f && /Condition 2/{print a; f=0}
Если
f
истинно и текущая строка содержитCondition 2
, то вывести переменнуюa
и сброситьf
ее на ноль.
решение2
Если вам нужна обратная адресация при обработке текста, используйтеex
ЕгоУказано POSIX, и это скриптовая форма vi
(и vi
непосредственный предшественник ) — очень гибкая.
printf '%s\n' 'g/Condition 2/?Condition 1?,.p' | ex output.out
Это означает:
Для каждой строки ( g
глобально), соответствующей шаблону «Условие 2», выполнить поиск в обратном направлении непосредственно предшествующего экземпляра «Условия 1» и p
вывести все строки от этой строки до текущей строки ( .
) (то есть строки с «Условием 2»).
Выходные данные на предоставленных входных данных точно такие, как вы описываете.
решение3
sed 'H;/PATTERN_1/h;/PATTERN_2/!d;x' infile
хотя это предполагает, что любой строке, которая соответствует, PATTERN_2
предшествует по крайней мере одна строка, соответствующая PATTERN_1
. Для более общего случая добавьте еще одно условие для проверки PATTERN_1
наличия в пространстве шаблона перед печатью:
sed 'H;/PATTERN_1/h;/PATTERN_2/!d;x;/PATTERN_1/!d' infile
решение4
Вот зловещий кусочек Perl:
perl -0777 -ne '
my $c1 = qr/Condition 1/;
my $c2 = qr/Condition 2/;
print for map {s/$c2.*?\n\K.*//s; $_}
grep {/$c2/}
split /(?=$c1)/ms;
' output.out
Это:
- читает весь файл (используя параметры
-0777
и-n
), - разделяет его там, где появляется Условие 1 (
split
), - отфильтровывает абзацы, в которых Условие 2 не появляется (
grep
), - затем удаляет из каждого интересного абзаца все строки, следующие за строкой Условие 2 (
map
).