Expresión regular usando \\ vs usando \

10

Por que

grep e\\.g\\. <<< "this is an e.g. wow"

y

grep e\.g\. <<< "this is an e.g. wow"

¿hacer la misma cosa?

Si agrego una tercera barra, también tiene el mismo resultado. PERO, una vez que agrego una cuarta barra, ya no funciona. Esto tiene que ver con una pregunta de un examen anterior para una clase. Preguntó si el que tiene dos barras invertidas funcionaría para generar la línea con "por ejemplo". Originalmente pensé que no funcionaría, pero traté de asegurarme y lo hizo. ¿Cuál es la explicación?

Wyatt Grant
fuente
Pensé que bash tomaría \\\.y daría grep, \.pero no es así. buena pregunta

Respuestas:

9

Primero, tenga en cuenta que la barra oblicua coincide demasiado:

$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
 e.g.

En lo que respecta a Bash , un período de escape es lo mismo que un período. Bash pasa el período a grep . Para grep, un punto coincide con cualquier cosa.

Ahora, considere:

$ echo $'eegg \n e.g.' | grep e\\.g\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\.g\\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\\.g\\\\.
$

Cuando Bash ve una doble barra, la reduce a una sola barra y la pasa a grep que, en la primera de las tres pruebas anteriores, ve, como queremos, una sola barra antes de un período. Por lo tanto, esto hace lo correcto.

Con una triple barra, Bash reduce las dos primeras a una sola barra. Entonces ve \.. Dado que un período de escape no tiene un significado especial para Bash, esto se reduce a un período simple. El resultado es que grep ve, como queremos, una barra antes de un período.

Con cuatro barras, Bash reduce cada par a una sola barra. Bash pasa a grep dos barras y un punto. grep ve las dos barras y un punto y reduce las dos barras a una sola barra literal . A menos que la entrada tenga una barra diagonal seguida de cualquier carácter, no hay coincidencias.

Para ilustrar eso último, recuerde que dentro de las comillas simples, todos los caracteres son literales. Por lo tanto, dadas las siguientes tres líneas de entrada, el comando grep solo coincide en la línea con la barra diagonal en la entrada:

$ echo 'eegg
e.g.
e\.g\.' |  grep e\\\\.g\\\\.
e\.g\.

Resumen del comportamiento de Bash

Para Bash, las reglas son

  • Dos barras se reducen a una sola barra.

  • Una barra diagonal frente a un carácter normal, como un punto, es solo el carácter normal (punto).

Así:

$ echo \. \\. \\\. \\\\.
. \. \. \\.

Hay una manera simple de evitar toda esta confusión: en la línea de comando Bash, las expresiones regulares deben colocarse entre comillas simples. Dentro de comillas simples, Bash deja todo solo.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.
John1024
fuente
Pregunta: Se necesitan dos barras invertidas para que bash lo vea como una barra invertida (una es la secuencia de escape, la otra es la barra invertida literal). Entonces, cuando hay 3, ¿bash trata al tercer rezagado como una secuencia de escape también? Como no escapa de nada, ¿se descarta?
Franz Kafka
@DanielAmaya El tercero se trata como un escape para el personaje que sigue. En nuestro caso, ese carácter es el período y, para bash (a diferencia de grep), un período escapado es solo un período simple. bash luego pasa el período simple a grep.
John1024
@DanielAmaya Vea la respuesta actualizada para una echodeclaración que ilustra lo que hace bash en estos casos.
John1024
2
@DanielAmaya En ambos casos, bash reduce Las dos primeras barras a una sola barra. Lo que queda es \.o .. Para bash, ambos son iguales: son equivalentes a un período simple. Por lo tanto, en total, lo que bash entrega a grep es lo mismo para ambos: una barra inclinada seguida de un punto.
John1024
1
Solo una pequeña adición: el uso echono es una forma muy confiable de probar regexp debido a la implementación de este programa. Por ejemplo, debajo de mi zsh (eco incorporado) echo \. \\. \\\. \\\\. \\\\\.da . \. \. \. \., pero /bin/echo \. \\. \\\. \\\\. \\\\\.regresa . \. \. \\. \\.. Algo así printf "%s" ...es probablemente la mejor manera.
jimmij
4

El resultado es el mismo solo para su cadena, pero en general esas expresiones regulares hacen cosas diferentes. Modifiquemos un poco su ejemplo agregando el segundo patrón e,g,(con comas), el tercero e\.g\.(puntos), el cuarto e\,g\,(comas) y la -oopción de grep para imprimir solo las partes coincidentes.

  • En el siguiente caso .coincidirá con cualquier char (aviso ''alrededor e.g., voy a llegar a eso más adelante)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • A continuación, escapamos .con una barra diagonal inversa \, por lo que solo .coincidirá el literal :

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • Pero podemos escapar \con otro \, para que el literal \coincida seguido de .(es decir, cualquier carácter):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Pero si queremos coincidir sólo \.no \,luego otro \se necesita para escapar significado especial del punto:

    $ grep -o 'e\\\.g\\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.

Ahora, debido a que no usó el ''argumento grep, debe agregar otras barras diagonales inversas para escapar de las barras diagonales inversas de la interpretación de shell, por lo tanto:

grep 'e\.g\.'     => grep e\\.g\\.
grep 'e\\.g\\.'   => grep e\\\\.g\\\\.  (each backslash has to be quoted separately)
grep 'e\\\.g\\\.' => grep e\\\\\\.g\\\\\\. (3 x 2 = 6 backslashes in total)
jimmij
fuente
3

Cuando haces un grep e\.g\., el shell está consumiendo la barra invertida, por lo que estás haciendo un grep e.g., que coincide. Cuando haces un grep e\\.g\\., el shell vuelve a consumir una barra oblicua, y ahora estás haciendo un grep e\.\g., que nuevamente coincide. Ahora, se ve una barra invertida en el shell \\. Entonces, cuando tiene \\, el primero es una secuencia de escape, el segundo es una barra diagonal inversa. Cuando haces un grep e\\\.g\\\., todavía termina siendo grep e\.\g., porque no hay una secuencia de escape ( \) antes de la primera \para que sea literal \. Tenga en cuenta que \ es una barra invertida, por lo que grep e\\\\.\\\\gtermina siendo grep e\\.g\\., lo que obviamente no coincide.

Para ver cómo el shell está viendo lo que estás haciendo, usa echo (por ejemplo, echo grep e\\.g\\. <<< "this is an e.g. wow"vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow")

Franz Kafka
fuente
0

Los dos comandos producen la misma salida solo para su entrada, pero de lo contrario son diferentes. Para comprender lo que está sucediendo, tenemos que saber cómo se interpreta el parámetro primero bashy luego por grep.

Escapando en bash

\es un carácter especial que cancela el significado especial del siguiente carácter, incluido él \mismo. Si el siguiente carácter no tiene un significado especial, se pasa sin cambios. Ejemplos con comando y un resultado:

  • echo \a: a- el personaje ordinario escapado le da al personaje
  • echo \\: \- el carácter especial escapado le da al personaje
  • echo \\\a: \a- combinación especial, ordinaria
  • echo \\\\: \\- combinación especial, especial

echoimprimirá la cadena resultante después de bashinterpretarla. Más información: documentación de fiesta , los piratas informáticos de bash wiki , especificación POSIX .

.no tiene ningún significado especial en bash. Es un personaje ordinario para el caparazón. A continuación se muestran las secuencias relevantes para sus ejemplos:

  • echo .: .
  • echo \.: .
  • echo \\.: \.
  • echo \\\.: \.
  • echo \\\\.: \\.

Solución más simple para cadenas literales en bash

Para pasar parámetros literalmente bash, puede usar el 'escape de comillas simples . Entre comillas simples no tiene que preocuparse por el significado especial de los caracteres porque la comilla simple es el único carácter con un significado especial allí. Puede insertar una comilla simple después de encerrar la primera parte de la cadena. Ejemplo
echo 'part1'\''part2': part1'part2

Regex en grep

\es un personaje de escape con un significado similar al de bash. .es un caracter especial que representa una sola ocurrencia de cualquier caracter . Ver: POSIX regex , GNU grep regex . Ejemplos de expresiones regex:

  • .- coincide con cualquier personaje como ao.
  • \.- coincide solo .literalmente

Sus ejemplos

En la segunda línea de todos los ejemplos a continuación encontrará equivalente entre comillas simples 'que muestran qué cadena literal se pasa por basha grep. Luego, después de greprealizar el escape, el único carácter especial posible en los ejemplos es hacer .coincidir cualquier carácter. En la tercera línea hay una descripción con la que coincide la expresión.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    ecualquier carácter gcualquier carácter - coincidencias e.g.y posiblemente otras cadenas comoeagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    ecualquier carácter gcualquier carácter - coincidencias e.g.y posiblemente otras cadenas comoexgy
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.literalmente - solo coincidee.g.
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.literalmente - solo coincidee.g.
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\cualquier personaje g\cualquier personaje - no coincidee.g.
pabouk
fuente