Сортировать текстовые файлы с несколькими строками как строки

Вопрос или проблема

У меня есть текстовый файл в таком формате:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Я хочу отсортировать этот файл по строке KEY и сохранить следующие 4 строки с ним в результате, так что отсортированный результат должен быть следующим:

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Есть ли способ это сделать?

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

Насколько мне известно, вы не можете использовать произвольный шаблон для записей, так что, если ваши записи фиксированной длины (в байтах, а не в символах или строках). msort действительно имеет опцию -b для записей, которые являются блоками строк, разделёнными пустыми строками.

Вы можете довольно легко преобразовать ваш ввод в формат, который будет работать с -b, добавив пустую строку перед каждой ###... (кроме первой).

По умолчанию он выводит статистику в stderr, так что, по крайней мере, легко понять, когда он не отсортировал, потому что думал, что весь ввод это одна запись.


msort работает с вашими данными. Команда sed добавляет новую строку перед каждой строкой #+, кроме первой. -w сортирует всю запись (лексикографически). Есть опции для выбора, какую часть записи использовать в качестве ключа, но они мне не понадобились.

Я также пропустил удаление лишних новых строк.

$ sed '2,$ s/^#\+/\n&/' unsorted.records | msort -b -w 2>/dev/null 
####################################
KEY1
VAL11
VAL12
VAL13
VAL14

####################################
KEY2
VAL21
VAL22
VAL23
VAL24

####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Мне не повезло с -r '#', чтобы использовать это в качестве разделителя записей. Он считал, что весь файл это одна запись.

Решением является сначала заменить символы переноса строки внутри блока на неиспользуемый символ по вашему выбору (‘|’ в примере ниже), чтобы отсортировать результат и затем вернуть выбранный разделитель обратно к оригинальному переносу строки:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'

perl -0777 -ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0777 -n захватывает весь файл (см. также perl -gn в более новых версиях perl).
  • /(....)/g соответствует и извлекает записи
  • print sort ... сортирует и выводит их

Вот другой способ, который должен работать с любым количеством строк в секции KEY:

# извлечение разделителя
delim=$(head -n1 <infile)
sed '/#/d;/KEY/h;G;s/\n/\x02/' infile | nl -ba -nrz -s $'\002' | sort -t $'\002' -k3 -k1,1 |
cut -d $'\002' -f2 | sed '/KEY/{x;s/.*/'"${delim}"'/;G}'

Это работает, сохраняя разделитель в переменной (чтобы затем удалить его из ввода). Затем он добавляет KEY* к каждой строке в соответствующем разделе, используя символ нижнего ASCII (который вряд ли появится в вашем вводе) в качестве разделителя, а затем nумеряет все lинии, используя тот же разделитель. Затем остается только sortировать по 3-му и 1-му полю и cutить средний столбец, а затем восстанавливать разделители с помощью последнего sed. Обратите внимание, что с приведённым выше примером KEY12 будет сортироваться перед KEY2, так что отрегулируйте команду sort в зависимости от ваших нужд.

Вы можете использовать POSIX Awk библиотеку stdlib:

#!/usr/local/bin/awklib -f
$0 ~ "#" {x++}
{q[x] = q[x] ? q[x] RS $0 : $0}
END {
  arr_sort(q)
  for (x in q) print q[x]
}

Используя Raku (ранее известный как Perl_6)

~$ raku -e 'slurp.match(:global, / ^^\#+ <-[\#]>* /).sort.join.trim-trailing.put;'  file

#ИЛИ:

~$ raku -e 'slurp.comb(/ ^^\#+ <-[\#]>* /).sort.join.trim-trailing.put;'  file

Пример входных данных:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Пример выходных данных:

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Вызов trim-trailing удаляет лишнюю \n новую строку, добавленную в конце. Если это проблематично, либо устраните это, либо используйте .chop / .chomp вместо этого.

Ответ или решение

Для сортировки текстовых файлов, содержащих многострочные записи, существует несколько подходов, которые можно применить. В данном случае мы будем рассматривать текстовый файл с заданным форматом и предоставим пошаговое руководство по сортировке его содержимого по строкам с ключами.

Проблема

Имеется текстовый файл следующего формата:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

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

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Решение

Существует несколько способов справиться с этой задачей, от использования стандартных утилит командной строки до написания скриптов. Давайте рассмотрим различные подходы.

Метод 1: Использование sed и sort

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

  1. Преобразование файла. Сначала добавим пустую строку перед каждой строкой, начинающейся на #, кроме первой.

    sed '2,$ s/^#\+/\n&/' unsorted.records > formatted.records
  2. Сортировка файла. Затем мы можем отсортировать файл на основе ключей.

    sort -k2,2 formatted.records > sorted.records
  3. Возврат к исходному формату. Теперь мы можем вернуть форматирование обратно:

    sed 's/\n/|/g' sorted.records | sort -t '|' -k2 | sed 's/|/\n/g'

Этот метод может быть немного сложным, но он использует стандартные утилиты Unix и может быть легко адаптирован.

Метод 2: Использование awk

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

awk -v RS="####################################" 'NR>1 {print $0}' file.txt | sort -t '\n' -k2,2

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

Метод 3: Использование perl

Если вам удобнее работать с perl, можно использовать следующий скрипт:

perl -0777 -ne 'print sort /(#+[^#]*)/g' file.txt

Этот код считывает весь файл и сортирует найденные блоки на основе ключей, присутствующих в каждом блоке.

Заключение

Каждый из приведенных методов имеет свои преимущества в зависимости от ваших предпочтений и требований к среде. Если вы ищете простоту и возможность работы с командной строкой, выберите первый или второй подход. Если вы предпочитаете мощность и гибкость, используйте perl.

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

Оцените материал
Добавить комментарий

Капча загружается...