Bash: ¿forma fácil de pasar una cadena “cruda” a grep?

grep no se puede alimentar con cadenas “crudas” cuando se utiliza desde la línea de comandos, ya que algunos caracteres deben escaparse para no tratarse como literales. Por ejemplo:

 $ grep '(hello|bye)' # WON'T MATCH 'hello' $ grep '\(hello\|bye\)' # GOOD, BUT QUICKLY BECOMES UNREADABLE 

Estaba usando printf para escapes automáticos:

 $ printf '%q' '(some|group)\n' \(some\|group\)\\n 

Esto produce una versión escapada de bash de la cadena, y al usar los trazos atrás, esto se puede pasar fácilmente a una llamada grep:

 $ grep `printf '%q' '(a|b|c)'` 

Sin embargo, es claro que no está destinado a esto: algunos caracteres en la salida no se escapan, y algunos son innecesariamente así. Por ejemplo:

 $ printf '%q' '(^#)' \(\^#\) 

El caracter ^ no debe escaparse cuando pasa a grep .

¿Hay una herramienta cli que toma una cadena sin procesar y devuelve una versión escapada de bash de la cadena que se puede usar directamente como patrón con grep ? ¿Cómo puedo lograr esto en pura bash, si no?

Si intenta hacer que grep use la syntax Expresión regular extendida, la forma de hacerlo es usar grep -E (aka egrep ). También debe saber acerca de grep -F (también fgrep como fgrep ) y, en las versiones más nuevas de GNU Coreutils, grep -P .

Antecedentes: el grep original tenía un conjunto bastante pequeño de operadores de expresiones regulares; fue la implementación original de la expresión regular de Ken Thompson. Una nueva versión con un repertorio ampliado se desarrolló más adelante, y por razones de compatibilidad, obtuvo un nombre diferente. Con GNU grep , solo hay un binario, que entiende la syntax RE tradicional básica, si se invoca como grep , y ERE si se invoca como egrep . Algunos constructos de egrep están disponibles en grep usando un escape de barra invertida para introducir un significado especial.

Posteriormente, el lenguaje de progtwigción Perl ha ampliado el formalismo aún más; este dialecto de expresiones regulares parece ser lo que la mayoría de los recién llegados erróneamente también esperan que grep apoye. Con grep -P , lo hace; pero esto aún no es ampliamente compatible en todas las plataformas.

Entonces, en grep , los siguientes caracteres tienen un significado especial: ^$[]*.\

En egrep , los siguientes caracteres también tienen un significado especial: ()|+?{} . (Los refuerzos para la repetición no estaban en el egrep original.) Los paréntesis de agrupación también permiten referencias posteriores con \1 , \2 , etc.

En muchas versiones de grep , puede obtener el comportamiento egrep poniendo una barra invertida antes de las especiales egrep . También hay secuencias especiales como \<\> .

En Perl, se introdujo una gran cantidad de escapes adicionales como \w \s \d . En Perl 5, la instalación de expresiones regulares se extendió sustancialmente, con concordancia no codiciosa *? +? etc, paréntesis que no agrupan (?:...) , lookaheads, lookbehinds, etc.

… Dicho esto, si realmente desea convertir egrep regulares de egrep para grep expresiones regulares sin invocar ningún proceso externo , pruebe ${regex/pattern/substitution} para cada uno de los caracteres especiales egrep ; pero reconozca que esto no maneja correctamente las clases de caracteres, las clases de caracteres negadas o las diagonales inversas.

Si quieres buscar una cadena exacta,

 grep -F '(some|group)\n' ... 

-F le dice a grep que trate el patrón como está, sin interpretación como expresión regular.

(Esto a menudo está disponible también como fgrep ).

Cuando uso grep -E con cadenas provistas por el usuario, las escapo con esto

 ere_quote() { sed 's/[]\.|$(){}?+*^]/\\&/g' <<< "$*" } 

ejemplo ejecutar

 ere_quote ' \ $ [ ] ( ) { } | ^ . ? + *' # output # \\ \$ \[ \] \( \) \{ \} \| \^ \. \? \+ \* 

De esta forma, puede insertar con seguridad la cadena entrecomillada en su expresión regular.

por ejemplo, si desea buscar cada línea comenzando con el contenido del usuario, con el usuario proporcionando cadenas divertidas como. *

 userdata=".*" grep -E -- "^$(ere_quote "$userdata")" <<< ".*hello" # if you have colors in grep you'll see only ".*" in red 

Creo que las respuestas anteriores no están completas porque se pierden una cosa importante, a saber, la cadena que comienza con el guión (-). Entonces, si bien esto no funcionará:

 echo "ABC" | grep -F "-B-" 

Este hará:

 echo "ABC" | grep -F -- "-B-"