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

13

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

####################################
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

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

RYN
источник
5
не пересекайте сообщение, пожалуйста
Zanna
@Zanna: я думаю, что есть исключения для разделов Unix и Askubuntu, поскольку эти два имеют много общего друг с другом! Я думаю, что читал об этом в мета-разделе Unix
RYN
2
соответствующий мета-вопрос, задаваемый здесь модом AU :) Как должны обрабатываться вопросы, кросс-посты в Ask Ubuntu?
Занна
@RYN Проблема не будет перекрываться, на самом деле, многие сайты SE пересекаются, но люди, которые дают ответы, могут не знать об ответах на другом сайте.
phk

Ответы:

12

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

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

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

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


msortработает на ваших данных. Команда sedдобавляет новую #+строку к каждой строке, кроме строки 1., -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 '#'использовать это как разделитель записей. Он думал, что весь файл был одной записью.

Питер Кордес
источник
большое Вам спасибо; msortочень полезно; спасибо (о, -rкажется, это потому, что есть более одного # я использовал, -dи это сработало
RYN
круто! (+1) msort -qwr '#' ex работает для меня (ну, это изменяет разделитель записи rec.)
JJoao
8

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

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
xhienne
источник
1
Благодарность; это работает, но это очень грязно, особенно когда данные грязные тоже! если после ключа было 100, то мне нужно поставить 100 ;N, и может быть трудно найти символ, который не используется в самом тексте; это очень хорошо для sortили awk... уметь выполнять многострочную сортировку
RYN
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 хлебает весь файл
  • /(....)/g сопоставить и извлечь записи
  • print sort ... сортировать и распечатывать их
JJoao
источник
2

Вот еще один способ работы с любым количеством строк в KEYразделе:

# extract delimiter
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все значения lines, используя один и тот же разделитель. В этом случае нужно только sortуказать 3-е и 1-е поле и cutсвязать средний столбец, а затем восстановить разделители с помощью финала sed. Обратите внимание, что с учетом вышеизложенного KEY12будет сортировать, прежде чем KEY2так настроить sortкоманду в соответствии с вашими потребностями.

don_crissti
источник
2

Вы можете использовать библиотеку 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]
}
Стивен Пенни
источник