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


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 , вставляет .


Обратите внимание, что в [mapcar sort ...]мы могли бы заменить sortфункцию, которая передает строки через внешний процесс. Затем мы можем получить инструмент для распределения внешней команды обработки текста по абзацам.
Каз
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.