Ordenar por expressões regulares

2

Eu tenho um conjunto de expressões regulares POSIX *

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

e um arquivo com linhas que correspondem a uma das expressões regulares:

BEGIN:VCARD
VERSION:3.0
N:Doe;Jane;;Ms;
URL:http://janedoe.com/
EMAIL:[email protected]
EMAIL:[email protected]
BDAY:1970-01-01
X-JABBER:[email protected]
X-ICQ:1234567890
END:VCARD

Eu gostaria de classificar essas linhas de acordo com

  1. o número da linha da correspondência de expressão regular (para que as linhas que começam com FN sejam apresentadas antes das linhas que começam com N),
  2. o grupo de correspondências (para que o X-ABC apareça antes do X-DEF)

Idealmente, as outras partes das linhas não devem ser classificadas (portanto, a sequência de linhas que começam com EMAIL deve ser deixada em paz). O resultado esperado deve, portanto, ser:

BEGIN:VCARD
VERSION:3.0
N:Doe;Jane;;Ms;
EMAIL:[email protected]
EMAIL:[email protected]
X-ICQ:1234567890
X-JABBER:[email protected]
BDAY:1970-01-01
URL:http://janedoe.com/
END:VCARD

Existe uma ferramenta existente para fazer isso?

Editar: Implementação resultante baseada em Resposta de Lars Rohrbach .

* Esta é a sequência de propriedades vCard em um arquivo de exportação de contatos do Gmail.

    
por l0b0 11.06.2012 / 02:40

2 respostas

3

O comando usual sort não fornece uma maneira incluída para especificar seu "dicionário" específico, e enquanto o comando grep permite que você forneça um arquivo de expressões regulares, ele não mudará a ordem do saída. Mas você pode colocar os dois juntos em um simples foreach loop - aqui está um exemplo que funciona no bash shell:

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

Isso pega cada linha regexp do seu arquivo de expressões regulares, uma a uma, e produz qualquer correspondência do seu arquivo de entrada, então a saída resultante será ordenada pela sua ordem regexp. Note que qualquer linha no seu arquivo de entrada que não corresponda a todos, não irá para a saída.

Adendo: Conforme solicitado, aqui está uma versão usando um while loop:

while IFS= read -r i; do grep "$i" myinputfile; done  < fileofregexp
    
por 11.06.2012 / 03:18
1

Não é exatamente assim que você o enquadra, mas dado o seu propósito real, seria mais simples pegar a parte antes do cólon e classificar por essa. Aqui está um script Perl que acumula linhas de chaves de classificação separadas em entradas de matriz separadas e libera um vcard quando chega ao fim.

#!/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;
}

E se você realmente quiser a flexibilidade completa do regexp, itere os regexps em vez de procurar uma chave em um hash.

#!/usr/bin/perl -n
BEGIN {
    @regexps = qw(^BEGIN:VCARD\r$ ^VERSION[^A-Z] ^FN[^A-Z] ^N[^A-Z] ... ^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} = ""; }
}
    
por 11.06.2012 / 03:42