Сортировка по регулярным выражениям

У меня есть набор регулярных выражений POSIX *

^BEGIN:VCARD\r$ ^VERSION[^AZ] ^FN[^AZ] ^N[^AZ] ^NICKNAME[^AZ] ^EMAIL[^AZ] ^X-\([AZ-]*\) ^TEL[^AZ] ^ADR[^AZ] ^ORG[^AZ] ^TITLE[^AZ] ^BDAY[^AZ] ^URL[^AZ] ^ROLE[^AZ] ^NOTE[^AZ] ^END:VCARD\r$ 

и файл с строками, каждый из которых соответствует одному из регулярных выражений:

 BEGIN:VCARD VERSION:3.0 N:Doe;Jane;;Ms; URL:http://janedoe.com/ EMAIL:jdoe@example.org EMAIL:jane.doe@janedoe.com BDAY:1970-01-01 X-JABBER:jane.doe@example.org X-ICQ:1234567890 END:VCARD - BEGIN:VCARD VERSION:3.0 N:Doe;Jane;;Ms; URL:http://janedoe.com/ EMAIL:jdoe@example.org EMAIL:jane.doe@janedoe.com BDAY:1970-01-01 X-JABBER:jane.doe@example.org X-ICQ:1234567890 END:VCARD - BEGIN:VCARD VERSION:3.0 N:Doe;Jane;;Ms; URL:http://janedoe.com/ EMAIL:jdoe@example.org EMAIL:jane.doe@janedoe.com BDAY:1970-01-01 X-JABBER:jane.doe@example.org X-ICQ:1234567890 END:VCARD 

Я бы хотел сортировать эти строки в соответствии с

  1. номер строки совпадения регулярных выражений (так что строки, начинающиеся с FN, поступают до строк, начинающихся с N),
  2. (так, чтобы X-ABC появился до X-DEF)

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

 BEGIN:VCARD VERSION:3.0 N:Doe;Jane;;Ms; EMAIL:jdoe@example.org EMAIL:jane.doe@janedoe.com X-ICQ:1234567890 X-JABBER:jane.doe@example.org BDAY:1970-01-01 URL:http://janedoe.com/ END:VCARD - BEGIN:VCARD VERSION:3.0 N:Doe;Jane;;Ms; EMAIL:jdoe@example.org EMAIL:jane.doe@janedoe.com X-ICQ:1234567890 X-JABBER:jane.doe@example.org BDAY:1970-01-01 URL:http://janedoe.com/ END:VCARD - BEGIN:VCARD VERSION:3.0 N:Doe;Jane;;Ms; EMAIL:jdoe@example.org EMAIL:jane.doe@janedoe.com X-ICQ:1234567890 X-JABBER:jane.doe@example.org BDAY:1970-01-01 URL:http://janedoe.com/ END:VCARD 

Существует ли для этого инструмент?

Изменить: Результирующая реализация основана на ответе Ларса Рорбаха .

* Это последовательность свойств vCard в файле экспорта контактов Gmail.

  • Сортировка блоков линий
  • добавить строку, в которой она принадлежит в отсортированном файле
  • Сортировка всех строк по горизонтали по ссылке на столбцы в определенной строке
  • Почему uniq игнорирует Unicode и строки с одной буквой?
  • Скрипт, который будет читать 5 чисел, а затем сортировать с самого высокого на самый низкий
  • Сортировка unix в алфавитном порядке, а затем в численном выражении, не работающая по назначению
  • Неверное поведение команды сортировки
  • Как сортировать коллекцию строк из разных файлов?
  • 2 Solutions collect form web for “Сортировка по регулярным выражениям”

    Обычная команда sort не предоставляет включенного способа указать ваш конкретный «словарь», и, хотя команда grep позволяет вам предоставить файл регулярных выражений, он не изменит порядок вывода. Но вы можете объединить оба в простой цикл foreach – вот пример, который работает в оболочке bash:

     for i in `cat fileofregexp`; do grep "$i" myinputfile; done 

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

    Приложение: в соответствии с запросом, вот версия с использованием цикла while:

     while IFS= read -ri; do grep "$i" myinputfile; done < fileofregexp 

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

     #!/usr/bin/perl -n BEGIN { @headers = qw(BEGIN VERSION FN N NICKNAME EMAIL X- TEL ADR ORG TITLE BDAY URL ROLE NOTE END); for $h (@headers) { $data{$h} = ""; } } if (/^([^:]+):/) { $data{exists $data{$1} ? $1 : "X-"} .= $_; if ($1 eq 'END') { for $h (@headers) { print $data{$h}; $data{$h} = ""; } } } else { print; } 

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

     #!/usr/bin/perl -n BEGIN { @regexps = qw(^BEGIN:VCARD\r$ ^VERSION[^AZ] ^FN[^AZ] ^N[^AZ] ... ^END:VCARD\r$); for $r (@regexps) { $data{$r} = ""; } } for $r (@regexps) { next unless $_ =~ $r; $data{$r} .= $_; last; } if ($_ =~ $regexps[@regexps-1]) { for $r (@regexps) { print "++", $data{$r}; $data{$r} = ""; } } 
    Linux и Unix - лучшая ОС в мире.