Expresión regular en script bash

13

Esta es mi primera vez bash scripting, así que probablemente estoy cometiendo un error fácil.

Básicamente, estoy tratando de escribir un script que obtenga los grupos de un usuario, y si están en un grupo determinado, lo registrará en consecuencia. Evidentemente habrá más funcionalidades, ¡pero no tiene sentido construir eso cuando ni siquiera puedo hacer que la expresión regular funcione!

Hasta ahora, tengo esto:

#!/bin/bash

regex="^([a-zA-Z0-9\-_]+ : [a-zA-Z0-9\-_]+) (usergroup)$"

# example output
groups="username : username usergroup"

echo "$groups" >> /home/jrdn/log

if [[ "$groups" =~ $regex ]]; then
    echo "Match!" >> /home/jrdn/log
else
    echo "No match" >> /home/jrdn/log
fi

Todos los lugares donde he probado esa expresión regular, funciona. Pero en el script bash, solo genera el $groups, seguido de No match. Entonces, ¿alguien puede decirme qué le pasa?

jrdn
fuente
1
¿Qué te hace pensar que hay algo malo en ello?
manatwork
1
@jrdnhannah luego intente recrear lentamente su expresión regular objetivo, primero coincida, ^([a-zA-Z0-9\-_]+)luego agregue los dos puntos y así sucesivamente ... debe averiguar muy pronto, ¿dónde está el problema?
Peter
2
Lo mismo aquí con bash 4.2.45. Escapar del guión bajo lo arregló. Extraño. @jrdnhannah, ¿podría escribir eso como respuesta y aceptarlo, por favor?
terdon
1
Como acabo de registrarme en Unix SE, me exige que espere 8 horas antes de contestar el mío. Aunque feliz de marcarlo como respondido si alguien más lo hace.
jrdn
44
@terdon bash simplemente llama a las funciones de expresiones regulares de libc, probablemente. Por lo tanto, depende de la versión de libc, no de la versión bash. Vea mi respuesta ... (O tal vez incluso en la secuencia de colación que tiene en uso)
derobert

Respuestas:

14

De man 7 regex:

Una expresión de paréntesis es una lista de caracteres encerrados en "[]". ...

... Para incluir un '-' literal, conviértalo en el primer o el último carácter ... [A] ll otros caracteres especiales, incluido '\', pierden su significado especial dentro de una expresión de paréntesis.

Probar la expresión regular con egrep da un error:

$ echo "username : username usergroup" | egrep "^([a-zA-Z0-9\-_]+ : [a-zA-Z0-9\-_]+) (usergroup)$"
egrep: Invalid range end

Aquí hay una versión más simple, que también da un error:

$ echo 'hi' | egrep '[\-_]'
egrep: Invalid range end

Como \no es especial, ese es un rango, tal como [a-z]lo sería. Debe poner su -al final, como [_-]o:

echo "username : username usergroup" | egrep "^([a-zA-Z0-9_-]+ : [a-zA-Z0-9_-]+) (usergroup)$"
username : username usergroup

Esto debería funcionar independientemente de su versión de libc (en egrep o bash).

editar: esto también depende de la configuración regional. La página de manual advierte sobre esto:

Los rangos dependen mucho de la secuencia de clasificación, y los programas portátiles deben evitar depender de ellos.

Por ejemplo:

$ echo '\_' | LC_ALL=en_US.UTF8 egrep '[\-_]'
egrep: Invalid range end
$ echo '\_' | LC_ALL=C egrep '[\-_]'
\_

Por supuesto, a pesar de que no falló, no está haciendo lo que quieres:

$ echo '\^_' | LC_ALL=C egrep '^[\-_]+$'
\^_

Es una gama, que en ASCII, incluye \, [, ^, y _.

derobert
fuente
Interesante. Mi egrepno da ningún error, solo coincide correctamente.
manatwork
@manatwork su secuencia de colación probablemente permita el rango ...
derobert
No sé mucho sobre colación. ¿Quieres decir esto LC_COLLATE="en_US.UTF-8"?
manatwork
@manatwork He editado la pregunta para dar un ejemplo. Tenga en cuenta que puede ser diferente en su sistema, porque a veces esas secuencias de clasificación (clasificación) cambian.
derobert
1
@manatwork Está bien, casi presenté un informe de error antes de notar el intento de escapar -...
derobert
4

Regla general con expresiones regulares (y cualquier error en piezas de código más grandes): córtela y reconstruya paso a paso o use bisección, lo que sea mejor para usted.

En este caso, el culpable resultó ser el guión bajo: escapar con una barra invertida lo ha hecho funcionar.

Peterph
fuente