usar expresión regular en condición if en bash

88

Me pregunto cuál es la regla general para usar expresiones regulares en la cláusula if en bash.

Aquí hay un ejemplo

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

¿Por qué los tres últimos no coinciden?

Espero que pueda dar tantas reglas generales como sea posible, no solo para este ejemplo.

Tim
fuente

Respuestas:

129

Cuando se usa un patrón glob, un signo de interrogación representa un solo carácter y un asterisco representa una secuencia de cero o más caracteres:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

Cuando se usa una expresión regular, un punto representa un solo carácter y un asterisco representa cero o más del carácter anterior. Entonces " .*" representa cero o más de cualquier carácter, " a*" representa cero o más "a", " [0-9]*" representa cero o más dígitos. Otro útil (entre muchos) es el signo más que representa uno o más de los caracteres anteriores. Entonces, " [a-z]+" representa uno o más caracteres alfabéticos en minúscula (en la configuración regional C y algunos otros).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi
Pausado hasta nuevo aviso.
fuente
Entonces, hay dos formas de hacer coincidir cadenas: ¿patrón glob y expresión regular? ¿Glob Pettern no solo se usa para nombres de archivos? En bash, ¿cuándo usar el patrón glob y cuándo usar la expresión regular? ¡Gracias!
Tim
1
@Tim: Globbing está disponible en la mayoría o en todas las versiones de Bash. La coincidencia de expresiones regulares está disponible solo en la versión 3 y superior, pero recomendaría usarla solo en 3.2 y posteriores. Las expresiones regulares son mucho más versátiles que el globbing.
Pausado hasta nuevo aviso.
14
if [[ $gg =~ ^....grid.* ]]
Ignacio Vázquez-Abrams
fuente
1
Debería poder utilizar ". {4}" en lugar de "....", es decir, "^. {4} grid. *". Puede ser más fácil de leer y comprender.
user276648
8

Agregar esta solución con incorporaciones grepbásicas shpara aquellos interesados ​​en una solución más portátil (independiente de la bashversión; también funciona con versiones antiguas sh, en plataformas que no son Linux, etc.)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

Algunas grepencarnaciones también admiten la -qopción (silenciosa) como alternativa a la redirección /dev/null, pero la redirección es nuevamente la más portátil.

vladr
fuente
olvidé un cierre ")" para egrep
ghostdog74
5
Utilizar en grep -qlugar de grep >/dev/null.
bfontaine
3

@OP,

¿Glob Pettern no solo se usa para nombres de archivos?

No, el patrón "glob" no solo se usa para los nombres de archivo. también puedes usarlo para comparar cadenas. En sus ejemplos, puede usar case / esac para buscar patrones de cadenas.

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

En bash, ¿cuándo usar el patrón glob y cuándo usar expresiones regulares? ¡Gracias!

Las expresiones regulares son más versátiles y "convenientes" que los "patrones glob", sin embargo, a menos que esté realizando tareas complejas que "globbing / globbing extendido" no puede proporcionar fácilmente, entonces no es necesario utilizar regex. Las expresiones regulares no son compatibles con la versión de bash <3.2 (como mencionó dennis), pero aún puede usar globbing extendido (por configuración extglob). para el globbing extendido, vea aquí y algunos ejemplos simples aquí .

Actualización para OP: Ejemplo para buscar archivos que comienzan con 2 caracteres (los puntos "." Significa 1 carácter) seguidos de "g" usando expresiones regulares

por ejemplo, salida

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

En lo anterior, los archivos coinciden porque sus nombres contienen 2 caracteres seguidos de "g". (es decir ..g).

El equivalente con globbing será algo como esto: (busque en la referencia el significado de ?y *)

$ for file in ??g*; do echo $file; done
abg
degree
..g
ghostdog74
fuente
Gracias ghostdog74. En Bash con una versión superior a 3.2, ¿se pueden usar expresiones regulares para reemplazar el patrón glob dondequiera que aparezca este último? ¿O la expresión regular solo se puede usar en algunas circunstancias especiales? Por ejemplo, encontré que "ls ?? g" funciona mientras que "ls ..g" no.
Tim
No hay nada que lo detenga por usar expresiones regulares si es necesario. Tu decides. Tenga en cuenta que la sintaxis de expresiones regulares es diferente de la sintaxis globbing de shell. entonces ls ..gno funciona. Le está diciendo al shell que busque un archivo con nombre ..g. En cuanto a aprender acerca de la sintaxis de expresiones regulares, se puede tratar perldoc perlretut, perldoc perlrequicko hacer una info seden la línea de comandos.
ghostdog74