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

У меня есть набор регулярных выражений 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.

  • Как численный сортировать по последнему столбцу?
  • Команда вроде «paste - -», но отсортированная в алфавитном порядке
  • Сортировка на основе поля1, field2, но сохранение поля 1 сортировки и игнорирование нулей / пробелов во 2-й сортировке
  • Сортировка unix в алфавитном порядке, а затем в численном выражении, не работающая по назначению
  • Сортировка строки datetime в 12-часовом формате
  • Утилита, полезная для работы с памятью, возвращает N первых отсортированных значений
  • Сортировка списков, когда нет начальных нулей
  • другой вопрос с сортировкой в ​​bash
  • 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} = ""; } } 
    Interesting Posts

    Рекурсивно сравнивать содержимое каталога по имени, игнорируя расширения файлов

    Принудительный клиент dhcp, чтобы получить новую аренду

    Несколько диапазонов VLAN на Openstack Liberty

    В чем разница между сравнением внутри, если использовать два или один равный (=)

    Как вы скрываете «Generic USB SD Reader» в nemo файловом менеджере, когда карты нет?

    Ошибка: iptables: нет цепочки / цели / соответствия этим именем

    Пакетное переименование файлов

    Как назначить значение входной переменной в оболочке

    mount -a блокирует мой скрипт, если какой-либо из многих удаленных дисков отключен

    Как запустить сценарий оболочки из любого места

    Как программно определить установленную максимальную версию ядра RPM?

    Черный экран после установки драйвера Nvidia

    Повторное использование старых рейдовых дисков

    Извлечение данных из текстового файла

    Sed для печати только первого соответствия шаблону линии

    Linux и Unix - лучшая ОС в мире.