Por que preciso escapar dos caracteres regex no sed para ser interpretado como caracteres regex?

11

Parece exemplo
cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
que eu deve escapar caracteres para formar uma expressão regular. Nesse caso, eu tive que escapar do aparelho para ser interpretado várias vezes.
Por quê? Eu esperava que tudo fosse um personagem regex, a menos que escapasse. Ou seja, o oposto.

Jim
fonte
Houve um post sobre pesquisa no Vim que aborda um pouco essa questão, a versão curta sendo "depende da implementação do comando" ... unix.stackexchange.com/questions/90345/…
Drav Sloan,
@DravSloan: Não tenho certeza de que é o mesmo. No Vim, você pesquisa texto por padrão e precisa escapar para pesquisar regex. Mas, neste caso, o formato s/regex//gespera uma regex e eu esperaria que fosse um texto que precisaria para escapar
Jim

Respostas:

14

Isso ocorre porque sedusa BREs POSIX (Expressões regulares básicas) em oposição aos EREs (expressões regulares estendidas) às quais você provavelmente está acostumado a partir de Perl ou amigos.

Na sed(1)página do manual:

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 no link acima:

O sabor Basic Regular Expressions ou BRE padroniza um sabor semelhante ao usado pelo comando grep tradicional do UNIX. Esse é praticamente o mais antigo sabor de expressão regular ainda em uso hoje. Uma coisa que diferencia esse sabor é que a maioria dos metacaracteres requer uma barra invertida para dar sabor ao metacaractere. A maioria dos outros tipos, incluindo o POSIX ERE, usa uma barra invertida para suprimir o significado de metacaracteres.

Citado literalmente no comentário de Craig Sanders :

Observe que, pelo menos no GNU sed, você pode dizer ao sed para usar regexps estendidos com a opção de linha de comando -r ou --regexp-extended. Isso é útil se você deseja evitar uglificar seu script sed com escape excessivo.

Joseph R.
fonte
1
Observe que, pelo menos no GNU sed, você pode dizer ao sed para usar regexps estendidos com a opção -rou --regexp-extendedlinha de comando. Isso é útil se você deseja evitar uglificar seu script sed com escape excessivo.
cas
@CraigSanders Obrigado por isso. Adicionado para responder.
Joseph R.
@CraigSanders, outras sedimplementações (quando suportam EREs, principalmente BSDs) tendem a usar -Epara isso (o que faz muito mais sentido, já que é a mesma opção que para grep. Por que o GNU sedescolheu -ré um mistério para mim).
Stéphane Chazelas
Sim, um mistério para mim também. Faria mais sentido usar -E. e então adicione -F, -G e -P para corresponder ao grep do GNU. O IMO gawk também se beneficiaria dos mesmos argumentos do ER ... ou, pelo menos, -P.
cas
12

Isso é por razões históricas.

O Regexp foi introduzido pela primeira vez no Unix no edutilitário no início dos anos 70. Embora edfoi baseada em qedcuja implementação pelos mesmos autores entendido regexp mais complexo, edsó entendeu ^, $, [...], ., *e \para escapar todos os itens acima.

Agora, quando surgiu a necessidade de ter mais operadores, era necessário encontrar uma maneira de apresentá-los sem interromper a compatibilidade com versões anteriores. Se um script usado para usar o s edcomando como s/foo() {/foo (var) {/gpara substituir todas as instâncias foo() {com foo(var) { e você introduziu um (ou {operador, que iria quebrar esse script.

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

Então, foi o que foi feito. Mais tarde, \(...\)foi adicionado inicialmente apenas para o s edcomando fazer coisas como s/foo\(.\)/\1bar/e mais tarde como grep '\(.\)\1'(mas ainda não coisas como \(xx\)*).

No UnixV7 (1979, quase uma década depois), uma nova forma de expressões regulares foi adicionada no novo egrepe os awkutilitários chamados expressão regular estendida (por serem novas ferramentas, não há compatibilidade com versões anteriores a serem quebradas). Por fim, ele forneceu a funcionalidade disponível no antigo de Ken Thompson qed(operador de alternância |, agrupamento (..)*) e adicionou alguns operadores como +e ?(mas não tinha o recurso de backref das expressões regulares básicas).

Posteriormente, os BSDs adicionaram \<e \>(para BRE e ERE) e o SysV adicionaram \{e \}somente para BREs.

Não é até muito mais tarde do que o ERE {e }foi adicionado ao ERE por essa compatibilidade com versões anteriores. Nem todo mundo adicionou. Por exemplo, o GNU awkaté a versão 4.0.0 (2011) não suportava, a {menos que fosse forçado a entrar no modo de conformidade POSIX.

quando GNU grepfoi escrita no início dos anos 90, é adicionado todos os presentes de ambos BSD e SysV (como \<, {) e em vez de ter duas sintaxe expreg separada e motor para ERB e ERE, implementados os mesmos operadores em ambos, apenas os homólogos BRE de (, ?, {, +tem que ser precedida por uma barra invertida (para ser compatível com outras implementações BRE). É por isso que você pode fazer .\+no GNU grep(embora isso não seja POSIX ou suportado por outras implementações) e você pode fazer (.)\1no GNU egrep(embora isso não seja POSIX ou seja suportado por muitas outras implementações, incluindo o GNU awk).

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

Stéphane Chazelas
fonte