Печать данных между двумя строками (только если существует «конец диапазона») из текстового файла

Мне нужно проанализировать файл, и я хочу напечатать сегмент данных между двумя конкретными строками. От «начала диапазона» до «конца диапазона», но только при наличии «конца диапазона».

Если исходный файл:

[This is the start] of some data this is information this is more information This is does not contain the ending required [This is the start] of some other data this is info I want this is info I want [This is the ending I was looking for] 

Следует напечатать:

 [This is the start] of some other data this is info I want this is info I want [This is the ending I was looking for] 

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

Учитывая, что количество строк данных не является постоянным, есть ли способ, с помощью которого я могу использовать grep или sed, чтобы работать с конечной строкой, чтобы найти следующее вхождение данной строки и захватить определенный диапазон, который я хочу?

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

Печать строк между (и включением) двумя рисунками не решает мою проблему, так как она начинает печатать на первой совпавшей строке и продолжает печатать до тех пор, пока не будет найден первый конечный сегмент. Мне нужно распечатать только те сегменты, которые содержат указанный оператор конца.

Используя sed :

 $ sed -n '/This is the start/{h;d;}; H; /This is the ending/{x;p;}' file [This is the start] of some other data this is info I want this is info I want [This is the ending I was looking for] 

Аннотированный сценарий sed :

 /This is the start/{ # We have found a start h; # Overwrite the hold space with it d; # Delete from pattern space, start next cycle }; H; # Append all other lines to the hold space /This is the ending/{ # We have found an ending x; # Swap pattern space with hold space p; # Print pattern space }; 

Сценарий выполняет сохранение всех строк в «пространство удержания» (буфер общего назначения в sed ), но как только мы находим «начальную строку», мы сбрасываем это пространство. Когда найдена «конечная строка», сохраненные данные печатаются.

Это прерывается, если перед «начальной линией» найдена «конечная линия», и, возможно, также, если найдены две «конечные линии» без промежуточной «начальной линии».


Программа awk которая проходит ту же процедуру, что и вышеприведенная программа sed :

 $ awk '/This is the start/ { hold = $0; next } { hold = hold ORS $0 } /This is the ending/ { print hold }' file 

(идентичный вывод, как указано выше)

С несколькими шаблонами START и END вы можете сделать это следующим образом:

 sed 'H;/START/h;/END/!d;x;/START/!d' infile 

Это безусловно накапливает строки в старом буфере H , перезаписывая его через h каждый раз, когда встречается строка START (то есть сохраняются только данные из самой последней строки START ), удаляя пространство шаблона, если оно не содержит Строка END (цикл перезапускается здесь), в противном случае, изменив буферы и снова, выбрав пространство шаблона, на этот раз, если оно не содержит START . Все, что осталось, печатается автоматически.

Используйте tac чтобы изменить порядок строк

Если вы используете tac для обращения к файлу – сначала для печати последней строки и т. Д. – тогда вы можете извлечь область из конечного шаблона в начальный шаблон. Затем снова используйте tac чтобы напечатать строки вывода в прямом порядке.

 tac file.txt | awk '/^\[This is the ending I was looking for]/,/^\[This is the start]/ { print $0 }' | tac 

Тот же код, отформатированный для лучшего размещения на экране:

 tac file.txt | \ awk '/^\[This is the ending I was looking for]/,/^\[This is the start]/ { print $0 }' | \ tac 

{ print $0 } не требуется в этой конкретной команде awk , так как это поведение по умолчанию:

 tac file.txt | \ awk '/^\[This is the ending I was looking for]/,/^\[This is the start]/' | \ tac 

К сожалению, если вы используете Mac, tac не устанавливается по умолчанию.

Вы можете сделать это с ex / vi или ed которые могут выполнять обратный поиск, например

  • поиск (вперед) для конечного паттерна
  • войдите в “нормальный” режим и поставьте отметку
  • отыщите оттуда исходный шаблон
  • печать с текущей строки до отметки

ех.

 $ ex file << \EOF /\[This is the ending I was looking for\] execute "normal! ma\" ?\[This is the start\] .,'ap EOF [This is the start] of some other data this is info I want this is info I want [This is the ending I was looking for] 

или же

 $ ed -s file << \EOF /\[This is the ending I was looking for\]/;# ka ?\[This is the start\]?;# .,'ap EOF [This is the start] of some other data this is info I want this is info I want [This is the ending I was looking for] 

Как однострочник:

 printf "/\[This is the ending I was looking for\]/;#\nka\n?\[This is the start\]?;#\n.,'ap\n" | ed -s file 

Решение с использованием awk:

 rstart='^[[]This is the start[]]' rend='[[]This is the ending I was looking for[]]' awk '$0~rstart{i=1;a=""} $0~rstart,$0~rend && i==1 {a = a ((a=="")?"":ORS) $0} $0~rend{i=0;print(a)} ' rstart="$rstart" rend="$rend" infile 

Квадратные скобки сопоставляются с помощью [[] и []] чтобы избежать использования обратной косой черты \\[ (которая может не работать при некоторых условиях).

Основная идея состоит в том, чтобы использовать переменную i (include) в качестве логического значения, чтобы включить или не включить каждую строку в диапазон для печати. Весь диапазон накапливается в переменной a . Разделяется ORS (разделителем выходных записей), если переменная a не равна нулю ( ((a=="")?"":ORS) ).

Это напечатает:

 [This is the start] of some other data this is info I want this is info I want [This is the ending I was looking for] 

Если требуется, чтобы начальный и конечный маркеры не печатались, используйте тот же код, но поменяйте строки 1 и 3:

 awk '$0~rend{i=0;print(a)} $0~rstart,$0~rend && i==1 {a = a ((a=="")?"":RS) $0} $0~rstart{i=1;a=""} ' rstart="$rstart" rend="$rend" infile 

Который напечатает:

 this is info I want this is info I want