¿Por qué necesito escapar de los caracteres regex en sed para ser interpretados como caracteres regex?

11

Parece, por ejemplo,
cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
que debo escapar de los caracteres para formar una expresión regular. En este caso, tuve que escapar de las llaves para ser interpretado varias veces.
¿Por qué? Esperaba que todo fuera un personaje regex a menos que escapara. Es decir, lo contrario.

Jim
fuente
Hubo una publicación sobre la búsqueda en Vim que de alguna manera cubre esta pregunta, la versión corta es "depende de la implementación del comando" ... unix.stackexchange.com/questions/90345/…
Drav Sloan
@DravSloan: No estoy seguro de que sea lo mismo. En Vim busca texto por defecto y necesita escapar para buscar expresiones regulares, pero en este caso el formato s/regex//gya espera una expresión regular y esperaría que sea texto que necesitaría para escapar
Jim

Respuestas:

14

Esto se debe a que sedutiliza BRE de POSIX (expresiones regulares básicas) en lugar de las ERE (expresiones regulares extendidas) a las que probablemente esté acostumbrado por Perl o sus amigos.

Desde la sed(1)página del 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 relevante del enlace de arriba:

El sabor Basic Regular Expressions o BRE estandariza un sabor similar al que usa el comando grep tradicional de UNIX. Este es el sabor de expresión regular más antiguo que todavía se usa en la actualidad. Una cosa que distingue este sabor es que la mayoría de los metacaracteres requieren una barra invertida para darle al metacaracteres su sabor. La mayoría de los otros sabores, incluido POSIX ERE, utilizan una barra diagonal inversa para suprimir el significado de los metacaracteres.

Citado textualmente del comentario de Craig Sanders :

Tenga en cuenta que en GNU sed al menos, puede decirle a sed que use expresiones regulares extendidas con la opción de línea de comando -r o --regexp-extended. Esto es útil si desea evitar uglificar su script sed con un escape excesivo.

Joseph R.
fuente
1
Tenga en cuenta que en GNU sed al menos, puede decirle a sed que use expresiones regulares extendidas con la opción de línea de comando -ro --regexp-extended. Esto es útil si desea evitar uglificar su script sed con un escape excesivo.
cas
@ CraigSanders Gracias por esto. Añadido a la respuesta.
Joseph R.
@CraigSanders, otras sedimplementaciones (cuando admiten ERE, en su mayoría BSD) tienden a usar -Epara eso en su lugar (lo que tiene mucho más sentido ya que esa es la misma opción que para grep. Por qué GNU sedeligió -res un misterio para mí).
Stéphane Chazelas
Sí, un misterio para mí también. Tendría más sentido usar -E. y luego agregue -F, -G y -P para que coincida con GNU grep. IMO gawk también se beneficiaría de los mismos argumentos RE ... o al menos, -P.
cas
12

Eso es por razones históricas.

Regexp se introdujo por primera vez en Unix en la edutilidad a principios de los años 70. Aunque edse basa en qedcuya aplicación por parte de los mismos autores entendieron expresión regular más complejo, edsólo se entiende ^, $, [...], ., *y \para escapar de todo lo anterior.

Ahora, cuando surgió la necesidad de tener más operadores, se tuvo que encontrar una manera de introducirlos sin romper la compatibilidad con versiones anteriores. Si un script solía usar el s edcomando s/foo() {/foo (var) {/gpara reemplazar todas las instancias de foo() {con foo(var) { y usted introdujo un operador (u {, eso rompería ese script.

Sin embargo, ningún script funcionaría s/foo\(\) {/foo\(var\) {/, porque es lo mismo s/foo() {/foo(var) {/y no había razón para escapar (ya que no era un operador RE. Por lo tanto, la introducción de un operador nuevo \(o \{no rompe la compatibilidad con versiones anteriores, ya que es muy poco probable que rompa un script existente utilizando la sintaxis anterior.

Entonces, eso fue lo que se hizo. Más tarde, \(...\)se agregó inicialmente solo para que el s edcomando haga cosas como s/foo\(.\)/\1bar/y luego como grep '\(.\)\1'(pero aún no cosas como \(xx\)*).

En UnixV7 (1979, casi una década después), se agregó una nueva forma de expresiones regulares en las nuevas egrepy las awkutilidades llamadas expresiones regulares extendidas (dado que son nuevas herramientas, no hay compatibilidad con versiones anteriores que se rompan). Finalmente, proporcionó la funcionalidad disponible en el antiguo Ken Thompson qed(operador de alternancia |, agrupación (..)*) y agregó algunos operadores como +y ?(pero no tenía la función de reflujo de las expresiones regulares básicas).

Más tarde, se agregaron BSD \<y \>(tanto a BRE como a ERE), y SysV se agregó \{y \}solo a BRE.

No es hasta mucho más tarde {y }se agregaron a ERE, al romper la compatibilidad con versiones anteriores. No todos lo agregaron. Por ejemplo, GNU awkhasta la versión 4.0.0 (2011) no era compatible a {menos que fuera forzado al modo de conformidad POSIX.

cuando GNU grepse escribió a principios de los 90, agregó todas las ventajas de BSD y SysV (como \<, {) , y en lugar de tener dos sintaxis regexp y un motor separados para BRE y ERE, implementó los mismos operadores en ambos, solo los equivalentes BRE de (, ?, {, +tiene que ser precedido por una barra invertida (para que sea compatible con otras implementaciones BRE). Es por eso que puede hacerlo .\+en GNU grep(aunque eso no es POSIX o no es compatible con otras implementaciones) y puede hacerlo (.)\1en GNU egrep(aunque eso no es POSIX ni es compatible con muchas otras implementaciones, incluida GNU awk).

Agregar \xoperadores no es la única forma de agregar más operadores de una manera compatible con versiones anteriores. Por ejemplo, perlusado (?...). Eso sigue siendo compatible con versiones anteriores de ERE, ya (?=...)que no es válido en ERE, lo mismo para .*?. vimpara operadores similares lo hizo de manera diferente introduciendo \@=o .\{-}por ejemplo.

Stéphane Chazelas
fuente