Как отсортировать блоки строк с определенной строкой из блока?

У меня есть файл с такими данными

BEGIN hello2 5 world1 END BEGIN hello4 2 world5 END BEGIN hello6 4 END 

Я хочу сортировать строки следующим образом, исходя из числа внутри блока. Числа изолированы и уникальны.

 BEGIN hello4 2 world5 END BEGIN hello6 4 END BEGIN hello2 5 world1 END 

Я знаю, как печатать блоки с sed и awk. Вот и все.

  # Prints the blocks including the BEGIN and END tags cat file | sed -n '/^BEGIN$/,/^END$/p' # Prints the blocks exluding the BEGIN and END tags awk '/^BEGIN$/ {show=1;next} /^END$/{show=0} { print }' file 

3 Solutions collect form web for “Как отсортировать блоки строк с определенной строкой из блока?”

Каждый раз, когда встречается строка BEGIN , отдельно прочитайте следующую цифровую строку из файла, используя отдельный дескриптор через getline . Распечатайте каждую строку из файла двумя префиксами, числовым значением, которое было ранее получено, и номером записи файла текущей записи (таким образом, все строки в одном блоке BEGIN .. END заканчиваются тем же значением в префиксе 1, соответствующем число, встроенное в блок). Подайте это на внешние утилиты sort и cut чтобы обрабатывать сортировку на основе префикса, следуя отбрасыванием префиксов.

 awk '/BEGIN/{"awk \\$0+0==\\$0 "FILENAME | getline x} {print x"~"FNR"~"$0 | "sort -k1,1n -k2,2n -t~ | cut -f3- -d~"}' file BEGIN hello4 2 world5 END BEGIN hello6 4 END BEGIN hello2 5 world1 END 

Использование GNU awk:

 gawk ' BEGIN { RS="\nEND\n"; ORS = RS; FS = "\n" } { record[$3] = $0 } END { PROCINFO["sorted_in"] = "@ind_num_asc" for (val in record) print record[val] } ' file 

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

PROCINFO определяет, как проходит массив записей. См. https://www.gnu.org/software/gawk/manual/html_node/Controlling-Scanning.html.

Первая строка объединяет текстовый блок, строчную линию, а также пытается найти номер для использования в качестве сортировки critea впоследствии. If-предложение if($0+0==$0) оценивает значение true, когда находит число.

Второй блок выполняется, когда он находит «END» на входе, поэтому он сохраняет блок в ассоциативном массиве, индексируя его, используя число, найденное в блоке.

 awk '{block=block"\n"$0; if($0+0==$0) num=$0;} /^END$/ {blks[num]=block; block=""} END {for(key in blks) print blks[key]}' file 

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

Например, посмотрите следующий скрипт awk :

 echo | awk '{a[2]="b"; a[1]="a"; a[3]="c"; for(key in a) print a[key];}' 

Он выводит:

 a b c 

В моем ответе я печатаю дополнительный \n перед каждым блоком, я полагаю, это не проблема. Вывод для вашего примера:

 BEGIN hello4 2 world5 END BEGIN hello6 4 END BEGIN hello2 5 world1 END 

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

 {if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}} 

Вот однострочная версия:

 awk '{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}} /^END$/ {blks[num]=block; block=""} END {for(key in blks) print blks[key]}' file 
  • Добавьте '.0' в целые числа с одной цифрой
  • Как удалить запятую и распечатать всю строку снова для слов, которые помещаются после запятой
  • Индексирование массива с использованием команды "sed"
  • Заменить \ n на новую строку в sed portably
  • Изменить значение в таблице
  • Добавить строку во многие файлы
  • Извлечение строк, содержащих PAT1, но не PAT2 с sed
  • использование awk для распаковки файлов в каталоге
  • Удалить определенное слово и пробел
  • Замените строки в файле на основе списка строк и списка соответствующих замещений
  • Отменить перемещение букв с помощью sed
  • Linux и Unix - лучшая ОС в мире.