Por que preciso escapar caracteres regex no sed para serem interpretados como caracteres regex?

10

Parece, por exemplo, cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
que eu devo escapar caracteres para formar uma expressão regular. Neste caso eu tive que escapar chaves, a fim de ser interpretado como um número de vezes.
Por quê? Eu estava esperando que tudo seria um caractere de regex a menos que escapasse. Ou seja o oposto.

    
por Jim 14.09.2013 / 20:28

2 respostas

13

Isso ocorre porque sed usa POSIX BREs (expressões regulares básicas) em oposição aos EREs (Extended Extended Expressions) Expressões Regulares) que você provavelmente já usou em Perl ou em amigos.

Na página sed(1) man:

REGULAR EXPRESSIONS
       POSIX.2 BREs should be supported, but they aren't completely because of
       performance problems.  The \n sequence in a regular expression  matches
       the newline character, and similarly for \a, \t, and other sequences.

Citação relevante do link acima:

The Basic Regular Expressions or BRE flavor standardizes a flavor similar to the one used by the traditional UNIX grep command. This is pretty much the oldest regular expression flavor still in use today. One thing that sets this flavor apart is that most metacharacters require a backslash to give the metacharacter its flavor. Most other flavors, including POSIX ERE, use a backslash to suppress the meaning of metacharacters.

Citações verbais de Comentário de Craig Sanders :

Note that in GNU sed at least, you can tell sed to use extended regexps with the -r or --regexp-extended command line option. This is useful if you want to avoid uglifying your sed script with excessive escaping.

    
por 14.09.2013 / 20:43
10

Isso é por razões históricas.

O Regexp foi introduzido pela primeira vez no Unix na utilidade ed no início dos anos 70. Embora ed tenha sido baseado em qed , cuja implementação pelos mesmos autores compreendia um regexp mais complexo, ed entendeu apenas ^ , $ , [...] , . , * e \ para escapar de todos os itens acima.

Agora, quando surgiu a necessidade de ter mais operadores, foi preciso encontrar uma maneira de apresentá-los sem quebrar a compatibilidade com versões anteriores. Se um script costumava usar o comando s ed como s/foo() {/foo (var) {/g para substituir todas as instâncias de foo() { por foo(var) { e você introduziu um operador ( ou { , isso quebraria esse script.

No entanto, nenhum script faria s/foo\(\) {/foo\(var\) {/ , porque isso é o mesmo que s/foo() {/foo(var) {/ e não havia razão para escapar de ( , pois esse não era um operador RE. Portanto, a introdução de um novo operador \( ou \{ não quebra a compatibilidade com versões anteriores, já que é muito improvável que ele quebre um script existente usando a sintaxe mais antiga.

Então, foi o que foi feito. Posteriormente, \(...\) foi adicionado inicialmente apenas para o comando s ed para executar ações como s/foo\(.\)/bar/ e posterior como grep '\(.\)' (mas não como \(xx\)* ).

No UnixV7 (1979, quase uma década depois), uma nova forma de expressões regulares foi adicionada nos novos utilitários egrep e awk chamados de expressão regular estendida (já que são novas ferramentas, não há compatibilidade retroativa com estar quebrado). Por fim, ele forneceu a funcionalidade disponível no antigo qed de Ken Thompson (operador de alternância | , agrupando (..)* ) e adicionou alguns operadores como + e ? (mas não tinha o backref característica das expressões regulares básicas).

Mais tarde, os BSDs adicionaram \< e \> (ao BRE e ERE), e o SysV adicionou \{ e \} apenas aos BREs.

Não é até muito mais tarde que { e } foram adicionados ao ERE, por tal quebra de compatibilidade retroativa. Nem todos adicionaram. Por exemplo, o GNU awk até a versão 4.0.0 (2011) não suportava { a menos que fosse forçado para o modo de conformidade POSIX.

quando o grep do GNU foi escrito no início dos anos 90, ele adicionou todos os benefícios do BSD e SysV (como \< , { ) e em vez de ter duas sintaxe e mecanismo de regexp separados para BRE e ERE, implementaram os mesmos operadores em ambos, apenas as contrapartes do BRE de ( , ? , { , + devem ser precedidas por uma barra invertida (para ser compatível com outras implementações do BRE). É por isso que você pode fazer .\+ no GNU grep (embora não seja POSIX ou suportado por outras implementações) e você pode fazer (.) no GNU egrep (embora não seja POSIX ou suportado por muitas outras implementações incluindo GNU awk ).

Adicionar \x operadores não é a única maneira de adicionar mais operadores de maneira compatível com versões anteriores. Por exemplo, perl usou (?...) . Isso ainda é compatível com EREs, pois (?=...) não é válido em EREs, mesmo para .*? . vim para operadores semelhantes fizeram isso de forma diferente, introduzindo \@= ou .\{-} , por exemplo.

    
por 16.09.2013 / 15:38