Pregunta:
Quiero determinar si una cadena de varias líneas termina con una línea que contiene un patrón específico.
Este código falló, no coincide.
s=`echo hello && echo world && echo OK`
[[ "$s" =~ 'OK$' ]] && echo match
Respuesta:
En bash
3.2 o superior y si la compatibilidad con 3.1 no está habilitada (con la opción compat31
o BASH_COMPAT=3.1
), BASH_COMPAT=3.1
operadores de expresión regular (no solo con \
sino con cualquiera de los operadores de comillas de bash
( '...'
, "..."
, $'...'
, $"..."
)) elimina su significado especial.
[[ $var =~ 'OK$' ]]
coincide solo en cadenas que contienen OK$
literalmente (ese $
coincide con un $
literal)
[[ $var =~ OK$ ]]
coincide con cadenas que terminan en OK
(ese $
es el operador RE que coincide al final de la cadena).
Eso también se aplica a las expresiones regulares almacenadas en variables o al resultado de alguna sustitución.
[[ $var =~ $regexp ]] # $var matches $regexp
[[ $var =~ "$string" ]] # $var contains $string
Tenga en cuenta que puede volverse incómodo porque hay algunos caracteres que necesita citar para la sintaxis del shell (como espacios en blanco, <
, >
, &
, paréntesis cuando no coinciden). Por ejemplo, si desea hacer coincidir con el .{3} <> [)}]&
regexp (3 caracteres seguidos de un " <> "
, ya sea a )
o }
y un &
), necesita algo como:
[[ $var =~ .{3}" <> "[}\)]\& ]]
En caso de duda sobre qué caracteres deben citarse, siempre puede utilizar una variable temporal . Eso también significa que hará que el código sea compatible con bash31
, zsh
o ksh93
:
pattern='.{3} <> [})]&'
[[ $var =~ $pattern ]] # remember *not* to quote $pattern here
Esa es también la única forma ( compat31
usar la opción compat31
(o BASH_COMPAT=3.1
)) de que puede hacer uso de los operadores extendidos que no son POSIX de las expresiones regulares de su sistema.
Por ejemplo, para que \<
sea tratado como el límite de la palabra que es en muchos motores de expresiones regulares, necesita:
pattern='\<word\>'
[[ $var =~ $pattern ]]
Haciendo:
[[ $var =~ \<word\> ]]
no funcionará ya que bash
trata a esos \
como operadores de comillas de shell y los elimina antes de pasar <word>
a la biblioteca regexp.
Tenga en cuenta que es mucho peor en ksh93 donde:
[[ $var =~ "x.*$" ]]
por ejemplo, coincidirá en whatever-xa*
pero no en whatever-xfoo
. La cita anterior elimina el significado especial de *
, pero no de .
ni $
.
El comportamiento de zsh
es más simple: las comillas no cambian el significado de los operadores de expresiones regulares allí (como en bash31), lo que lo convierte en un comportamiento más predecible (también puede usar expresiones regulares de PCRE en lugar de ERE (con set -o rematchpcre
)).
yash
no tiene una [[...]]
construcción, pero su [
incorporado tiene un operador =~
(también en zsh
). Y, por supuesto, [
ser un comando normal, las comillas no pueden afectar la forma en que se interpretan los operadores de expresiones regulares.
También tenga en cuenta que estrictamente hablando, su $s
no contiene 3 líneas, sino 2 líneas completas seguidas de una línea sin terminar. Contiene hello\nworld\nOK
. En la expresión regular extendida OK$
, el operador $
solo coincidiría al final de la cadena .
En una cadena de 3 líneas completas, como hello\nworld\nOK\n
(que no podría obtener con la sustitución de comandos, ya que la sustitución de comandos elimina todos los caracteres de nueva línea al final), $
coincidiría después de \n
, por lo que OK$
no coincidiría con él.
Con zsh -o pcrematch
sin embargo, el $
partidos tanto en el extremo de la cuerda y antes de que la nueva línea al final de la cadena si hay uno, ya que no pasa la PCRE_DOLLAR_ENDONLY
bandera para pcre_compile
. Eso podría verse como una mala idea ya que, en general, las variables en los shells no contienen un carácter de nueva línea al final, y cuando lo hacen, generalmente queremos que se consideren como datos .