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

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

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 
  • Чтение заданных строк в файле - BASH
  • Заменить первый столбец файла с помощью вывода команды
  • Добавить и выписать файл с переменными именами из списка переменных
  • Найти и заменить строки в текстовом файле на выходе из другого файла
  • Rsync-каталог, отфильтровывающий файлы и сохраняющие края (например, имена w / hyphen (-) chars)
  • Подсчет количества строк, имеющих число больше 100
  • Сортировка списка и отдельных элементов с запятыми вместо строк новой строки
  • сопоставление sed шаблонов с удалением по строке
  • Как отобразить только последнюю строку списка?
  • Как заполнить конечные теги с помощью sed, awk или любой другой команды?
  • Что значит . совпадение?
  • Linux и Unix - лучшая ОС в мире.