Вопрос или проблема
У меня есть текстовый файл в таком формате:
####################################
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
Этот метод включает преобразование входных данных в подходящий формат, который можно легко отсортировать, а затем возврат к исходному формату.
-
Преобразование файла. Сначала добавим пустую строку перед каждой строкой, начинающейся на
#
, кроме первой.sed '2,$ s/^#\+/\n&/' unsorted.records > formatted.records
-
Сортировка файла. Затем мы можем отсортировать файл на основе ключей.
sort -k2,2 formatted.records > sorted.records
-
Возврат к исходному формату. Теперь мы можем вернуть форматирование обратно:
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
.
Кроме того, перед выполнением любой вышеуказанной команды, убедитесь, что у вас есть резервная копия оригинального файла, чтобы избежать потери данных в случае ошибки.