Групповая сортировка непрерывных абзацев (разделенных пустой строкой)?

8

Я думаю, что теперь у меня довольно большой опыт сортировки по столбцам ; однако я пока не нашел ничего, как сортировать непрерывные строки .

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

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Теперь можно ли сортировать строки буквенно-цифрово для каждого блока в отдельности ? Я имею в виду, что результат выглядит так:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Судя по тому, что я нашел на sortстранице руководства , это может быть невозможно с помощью встроенной sortкоманды UNIX . Или это можно сделать, не прибегая к внешним / сторонним инструментам?

ошибка синтаксиса
источник

Ответы:

9

Решение Драва awkхорошо, но это означает выполнение одной sortкоманды на каждый абзац. Чтобы избежать этого, вы можете сделать:

< file awk -v n=0 '!NF{n++};{print n,$0}' | sort -k1n -k2 | cut -d' ' -f2-

Или вы можете сделать все это в perl:

perl -ne 'if (/\S/){push@l,$_}else{print sort@l if@l;@l=();print}
          END{print sort @l if @l}' < file

Обратите внимание, что выше разделители - это пустые строки (для awkодной строки, содержащие только пробел или символы табуляции, для perlодной - любой горизонтальный или вертикальный интервал) вместо пустых строк. Если вы хотите пустые строки, вы можете заменить !NFна !lengthили $0=="", и /\S/с /./.

Стефан Шазелас
источник
Спасибо и вам, особенно за awkрешение, которое позволяет избежать sortнакладных расходов! Подлый!
синтаксическая ошибка
9
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Установка разделителя записей RSна пустую строку делает шаг awk по абзацам за раз. Для каждого абзаца передайте абзац (in $0) на cmd (который установлен на sort) и распечатайте вывод. Распечатайте пустую строку, чтобы отделить выходные абзацы с помощью print "".

Если мы приводим примеры на Perl, я представляю альтернативный подход, чем подход Стефана:

perl -e 'undef $/; print join "\n", sort (split /\n/), "\n" 
    foreach(split(/\n\n/, <>))' < file

Отключите разделитель полей ( undef $/), это позволяет нам использовать <>и получать весь STDIN. Мы то splitчто вокруг \n\n(абзацы). foreach«абзац», sortстроки, splitперебирая новые строки, соединяя их, sortа затем joinсоединяя их и следуя за трейлингом \n.

Тем не менее, это имеет один побочный эффект добавления разделителя «конечный абзац» в последний абзац (если у него не было раньше). Вы можете обойти это с немного менее симпатичным:

perl -e 'undef $/; print join "\n", sort (split /\n/) , (\$_ == \$list[-1] ? "" : "\n")
    foreach(@list = split(/\n\n/, <>))' < file

Это назначает абзацы @list, и затем есть «троичная операция», чтобы проверить, является ли это последним элементом foreach( \$_ == \$list[-1]проверка). печать, ""если это ( ? ...), иначе ( : ...) печать "\n"для всех других «абзацев» (элементов @list).

Драв Слоан
источник
Это аккуратно! Спасибо. Вы действительно вызываете /usr/bin/sortэту строку или это awkвстроенная команда сортировки?
синтаксическая ошибка
Вызывая команду сортировки, отсюда и требование закрывать (cmd) на каждом цикле :)
Drav Sloan
5

Я написал инструмент на haskell, который позволяет вам использовать sort, shuf, tac или любую другую команду для абзацев текста.

https://gist.github.com/siers/01306a361c22f2de0122
РЕДАКТИРОВАТЬ: инструмент также включен в этот репо: https://github.com/siers/haskell-import-sort

Он разбивает текст на блоки, соединяет субблоки с \0символами, передает команду и, наконец, делает то же самое в обратном порядке.

28-08-2015 : я нашел другое, личное использование для этого инструмента - выбор N абзацев после строки.

paramap grep -aA2 '^reddit usernames' < ~/my-username-file
reddit usernames

foo
bar
baz

a couple
more of these
Райтис Вейнбахс
источник
4

Если у вас есть GNU awk, вы можете отсортировать каждый блок, используя встроенную asort()функцию. Что-то вроде этого:

blocksort.awk

function sort_n_print(array) {
  asort(array)
  for(i=1; i<=length(array); i++)
    print array[i]
  delete array
}

NF { a[++x] = $0 }

!NF { sort_n_print(a); print }

END { sort_n_print(a) }

Запустите это так:

awk -f blocksort.awk infile
Тор
источник
1

TXR Lisp пошагово:

$ cat data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(get-lines)' < data
("Echo" "Alpha" "Delta" "Charlie" "" "Golf" "Bravo" "Hotel" "Foxtrot")

$ txr -t '(get-lines)' < data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(partition* (get-lines) (op where [chain length zerop]))' < data
(("Echo" "Alpha" "Delta" "Charlie") ("Golf" "Bravo" "Hotel" "Foxtrot"))

$ txr -p '[mapcar sort (partition* (get-lines) (op where [chain length zerop]))]' < data
(("Alpha" "Charlie" "Delta" "Echo") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -p '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
(("Alpha" "Charlie" "Delta" "Echo") ("") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -t '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Ссылки: Get-линия , раздел * , оп , где , цепь , длина , zerop , MAPCAR , вставляет .

Kaz
источник
Обратите внимание, что в [mapcar sort ...]мы могли бы заменить sortфункцию, которая передает строки через внешний процесс. Затем мы можем получить инструмент для распределения внешней команды обработки текста по абзацам.
Каз