Coincidencia de expresiones regulares en una declaración if de Bash

86

¿Qué hice mal aquí?

Intentando hacer coincidir cualquier cadena que contenga espacios, minúsculas, mayúsculas o números. Los personajes especiales también estarían bien, pero creo que eso requiere escapar de ciertos personajes.

TEST="THIS is a TEST title with some numbers 12345 and special char *&^%$#"

if [[ "$TEST" =~ [^a-zA-Z0-9\ ] ]]; then BLAH; fi

Obviamente, esto solo prueba para números superiores, inferiores, y espacios. Aunque no funciona.

* ACTUALIZAR *

Supongo que debería haber sido más específico. Aquí está la línea real de código real.

if [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; fi

* ACTUALIZAR *

./anm.sh: line 265: syntax error in conditional expression
./anm.sh: line 265: syntax error near `&*#]'
./anm.sh: line 265: `  if [[ ! "$TITLE" =~ [a-zA-Z0-9 $%^\&*#] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; return; fi'
Atomiklan
fuente
¿Qué caparazón estás usando realmente? / bin / sh? / bin / bash? / bin / csh?
Willem Van Onsem
8
Es más seguro poner la expresión regular en una variable. re='...whatever...'; [[ $string =~ $re ]](sin comillas, este es uno de los raros casos en los que romperán algo que funcionaría sin ellas).
Charles Duffy
3
En su lugar, coloque comillas simples alrededor de la tarea. Las comillas dobles no protegerán adecuadamente los caracteres especiales.
tripleee
¡Muchas gracias Charles! Todavía está bien no ponerlo en una variable, ¡pero NO se debe citar en absoluto! Por ejemplo:[[ $var =~ .* ]] para coincidir con expresiones regulares .*(cualquier cosa). Supongo que si usa comillas, las citas en sí mismas se consideran parte de la expresión regular…
Stéphane
4
resumen de gotcha que encontré: (1.) guarde el patrón en una variable usando comillas simples pattern='^hello[0-9]*$'(2.) en la expresión de doble cuadrado si necesita coincidencia de expresiones regulares NO cite el patrón porque las citas DESACTIVAN la coincidencia de patrones de expresiones regulares. (es decir, la expresión [[ "$x" =~ $pattern ]]coincidirá con el uso de expresiones regulares y la expresión [[ "$x" =~ "$pattern" ]]deshabilita la coincidencia de expresiones regulares y es equivalente a[[ "$x" == "$pattern" ]] ).
Trevor Boyd Smith

Respuestas:

177

Hay un par de cosas importantes que debes saber sobre la [[ ]]construcción de bash . El primero:

La división de palabras y la expansión de nombre de ruta no se realizan en las palabras entre [[y ]]; Se realizan expansión de tilde, expansión de parámetros y variables, expansión aritmética, sustitución de comandos, sustitución de procesos y eliminación de comillas.

La segunda cosa:

Un operador binario adicional, '= ~', está disponible, ... la cadena a la derecha del operador se considera una expresión regular extendida y se empareja en consecuencia ... Cualquier parte del patrón se puede citar para forzar su coincidencia como una cuerda .

En consecuencia, $ven cualquier lado de =~se expandirá al valor de esa variable, pero el resultado no será dividido por palabras ni expandido por nombre de ruta. En otras palabras, es perfectamente seguro dejar las expansiones variables sin comillas en el lado izquierdo, pero debe saber que las expansiones variables ocurrirán en el lado derecho.

Entonces, si escribe:, [[ $x =~ [$0-9a-zA-Z] ]]el $0interior de la expresión regular a la derecha se expandirá antes de que se interprete la expresión regular, lo que probablemente hará que la expresión regular no se compile (a menos que la expansión de $0extremos con un dígito o símbolo de puntuación cuyo valor ascii sea menor que un dígito). Si cita el lado derecho así [[ $x =~ "[$0-9a-zA-Z]" ]], entonces el lado derecho se tratará como una cadena ordinaria, no como una expresión regular. (y $0aún se expandirá). Lo que realmente quieres en este caso es[[ $x =~ [\$0-9a-zA-Z] ]]

De manera similar, la expresión entre [[y ]]se divide en palabras antes de que se interprete la expresión regular. Por lo tanto, los espacios en la expresión regular deben escaparse o citarse. Si quería hacer coincidir letras, dígitos o espacios que podría utilizar: [[ $x =~ [0-9a-zA-Z\ ] ]]. De manera similar, otros caracteres deben tener un escape, como #, que iniciaría un comentario si no se cita. Por supuesto, puede poner el patrón en una variable:

pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...

Para las expresiones regulares que contienen muchos caracteres que necesitarían ser escapados o entre comillas para pasar por el lexer de bash, mucha gente prefiere este estilo. Pero cuidado: en este caso, no puede citar la expansión de la variable:

# This doesn't work:
if [[ $x =~ "$pat" ]]; then ...

Finalmente, creo que lo que está tratando de hacer es verificar que la variable solo contiene caracteres válidos. La forma más sencilla de realizar esta comprobación es asegurarse de que no contiene un carácter no válido. En otras palabras, una expresión como esta:

valid='0-9a-zA-Z $%&#' # add almost whatever else you want to allow to the list
if [[ ! $x =~ [^$valid] ]]; then ...

! niega la prueba, convirtiéndola en un operador "no coincide", y un [^...] clase de carácter regex significa "cualquier carácter que no sea ...".

La combinación de la expansión de parámetros y los operadores de expresiones regulares puede hacer que la sintaxis de la expresión regular de bash sea "casi legible", pero todavía existen algunos errores. (¿No son siempre?) Una de ellas es que no se podía poner ]en $valid, incluso si $validfueron citados, excepto en el principio. (Esa es una regla de expresiones regulares de Posix: si desea incluir ]en una clase de carácter, debe ir al principio. -Puede ir al principio o al final, por lo que si necesita ambos ]y -, debe comenzar con ]y terminar con -, que conduce a la expresión regular "yo sé lo que estoy haciendo" emoticon: [][-])

rici
fuente
6
Solo quiero señalar que "! ~ Es" no coincide con "operador" no es cierto. O use if ! [[ $x =~ $y ]]oif [[ ! $x =~ $y ]]
alcohol
shellchecker no está de acuerdo ...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
Leonardo
4
@leonard: ¿en qué se diferencia eso de mi declaración "no se puede citar la expansión de la variable" y el comentario "Esto no funciona"? ¿Qué no está claro sobre eso?
rici
1
@jinbeomhong: la expresión en sí se separa en palabras como de costumbre, usando espacios en blanco. Pero las expansiones de parámetros y comandos no están divididas por palabras.
rici
1
@jinbeomhong: No estoy diciendo nada diferente al manual de bash. "las palabras entre [[y ]]" se analizan del texto del programa, de la misma manera que las líneas de comando se analizan en palabras. Sin embargo, a diferencia de las líneas de comando, las palabras no se dividen después de las expansiones.
rici
26

En caso de que alguien quisiera un ejemplo usando variables ...

#!/bin/bash

# Only continue for 'develop' or 'release/*' branches
BRANCH_REGEX="^(develop$|release//*)"

if [[ $BRANCH =~ $BRANCH_REGEX ]];
then
    echo "BRANCH '$BRANCH' matches BRANCH_REGEX '$BRANCH_REGEX'"
else
    echo "BRANCH '$BRANCH' DOES NOT MATCH BRANCH_REGEX '$BRANCH_REGEX'"
fi
Oliver Pearmain
fuente
13

Preferiría usar [:punct:]para eso. Además, a-zA-Z09-9podría ser solo [:alnum:]:

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]
konsolebox
fuente