¿Cómo puedo hacer coincidir una cadena con una expresión regular en Bash?

166

Estoy tratando de escribir un script bash que contiene una función de modo que cuando dado un .tar, .tar.bz2, .tar.gzarchivo, etc. que utiliza el alquitrán con los interruptores pertinentes para descomprimir el archivo.

Estoy usando if elif y luego declaraciones que prueban el nombre de archivo para ver con qué termina y no puedo hacer que coincida con metacaracteres regex.

Para guardar la reescritura constante del script que estoy usando 'prueba' en la línea de comando, pensé que la siguiente declaración debería funcionar, he intentado todas las combinaciones de corchetes, citas y metacaracteres posibles y aún falla.

test sed-4.2.2.tar.bz2 = tar\.bz2$; echo $?
(this returns 1, false)

Estoy seguro de que el problema es simple y he buscado en todas partes, sin embargo, no puedo entender cómo hacerlo. ¿Alguien sabe cómo puedo hacer esto?

usuario1587462
fuente

Respuestas:

268

Para hacer coincidir las expresiones regulares, debe utilizar el =~operador.

Prueba esto:

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

Alternativamente, puede usar comodines (en lugar de expresiones regulares) con el ==operador:

[[ sed-4.2.2.tar.bz2 == *tar.bz2 ]] && echo matched

Si la portabilidad no es una preocupación, recomiendo usar en [[lugar de [o testporque es más seguro y más potente. Consulte ¿Cuál es la diferencia entre prueba, [y [[? para detalles.

dogbane
fuente
77
Tenga cuidado con la combinación de comodines glob en el segundo ejemplo. Dentro de [[]], el * no se expande como suele ser, para coincidir con los nombres de archivo en el directorio actual que coinciden con un patrón. Su ejemplo funciona, pero es realmente fácil generalizar en exceso y creer erróneamente que * significa unir cualquier cosa en cualquier contexto Solo funciona así dentro de [[]]. De lo contrario, se expande a los nombres de archivo existentes.
Alan Porter
77
Traté de usar comillas en la expresión regular y fallé; esta respuesta ayudó a hacer que esto funcione, check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fitenemos que almacenar la expresión regular en una var
Aquarius Power
También tener en cuenta que regexp (como en perl) NO debe estar entre paréntesis: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]no funcionaría.
pevik
18
FWIW, la sintaxis para la negación (es decir , no coincide ) es [[ ! foo =~ bar ]].
Skippy le Grand Gourou
1
dash no admite el -n 1parámetro, tampoco lo coloca automáticamente en una $REPLYvariable. ¡Cuidado!
54

Una función para hacer esto

extract () {
  if [ -f $1 ] ; then
      case $1 in
          *.tar.bz2)   tar xvjf $1    ;;
          *.tar.gz)    tar xvzf $1    ;;
          *.bz2)       bunzip2 $1     ;;
          *.rar)       rar x $1       ;;
          *.gz)        gunzip $1      ;;
          *.tar)       tar xvf $1     ;;
          *.tbz2)      tar xvjf $1    ;;
          *.tgz)       tar xvzf $1    ;;
          *.zip)       unzip $1       ;;
          *.Z)         uncompress $1  ;;
          *.7z)        7z x $1        ;;
          *)           echo "don't know '$1'..." ;;
      esac
  else
      echo "'$1' is not a valid file!"
  fi
}

Otra nota

En respuesta a Aquarius Power en el comentario anterior, We need to store the regex on a var

La variable BASH_REMATCH se establece después de que coincida con la expresión, y $ {BASH_REMATCH [n]} coincidirá con el enésimo grupo entre paréntesis, es decir, en el siguiente ${BASH_REMATCH[1]} = "compressed"y${BASH_REMATCH[2]} = ".gz"

if [[ "compressed.gz" =~ ^(.*)(\.[a-z]{1,5})$ ]]; 
then 
  echo ${BASH_REMATCH[2]} ; 
else 
  echo "Not proper format"; 
fi

(La expresión regular anterior no está destinada a ser válida para nombres y extensiones de archivos, pero funciona para el ejemplo)

dualidad
fuente
también tenga en cuenta que con BSD tar puede usar "tar xf" para todos los formatos y no necesita comandos separados o esta función en absoluto.
Buena persona
aen GNU tar o pen BSD tar para indicarle explícitamente que infiera automáticamente el tipo de compresión de la extensión. GNU tar no lo hará automáticamente de lo contrario, y supongo por el comentario de @GoodPerson que BSD tar lo hace por defecto.
Mark K Cowan
7z puede desempaquetar ... AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS , UDF, UEFI, VDI, VHD, VMDK, WIM, XAR y Z. ver 7-zip.org
mosh
14

No tengo suficiente representante para comentar aquí, así que estoy enviando una nueva respuesta para mejorar la respuesta de Dogbane. El punto . en la expresión regular

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

en realidad coincidirá con cualquier carácter, no solo el punto literal entre 'tar.bz2', por ejemplo

[[ sed-4.2.2.tar4bz2 =~ tar.bz2$ ]] && echo matched
[[ sed-4.2.2.tar§bz2 =~ tar.bz2$ ]] && echo matched

o cualquier cosa que no requiera escapar con '\'. La sintaxis estricta debería ser

[[ sed-4.2.2.tar.bz2 =~ tar\.bz2$ ]] && echo matched

o puede ir aún más estricto y también incluir el punto anterior en la expresión regular:

[[ sed-4.2.2.tar.bz2 =~ \.tar\.bz2$ ]] && echo matched
usuario2066480
fuente
9

Como está utilizando bash, no necesita crear un proceso secundario para hacerlo. Aquí hay una solución que lo realiza completamente dentro de bash:

[[ $TEST =~ ^(.*):\ +(.*)$ ]] && TEST=${BASH_REMATCH[1]}:${BASH_REMATCH[2]}

Explicación: El operador de coincidencia de patrones almacena los grupos antes y después de la secuencia "dos puntos y uno o más espacios" en la matriz BASH_REMATCH.

usuario1934428
fuente
1
Tenga en cuenta que el índice 0 contiene la coincidencia completa y los índices 1 y 2 contienen las coincidencias de grupo.
Rainer Schwarze el
3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

¡Funciona para mi! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)

juan cortez
fuente
1
Esto es extremadamente peligroso; solo se comporta sin un comportamiento indefinido porque no tiene archivos en el directorio actual llamado "patrón" de subcadena literal. Siga adelante, cree algunos archivos con ese nombre, y la expansión de subcadenas coincidirá con los archivos y romperá todo horriblemente con errores multicolores.
i336_
Pero he hecho un experimento: con los archivos `1 patrón , patrón patrón2 y patrón en el directorio actual. Este script funciona como se esperaba. ¿Podría por favor proporcionarme el resultado de su prueba? @ i336_
juan cortez
2
@ i336: No lo creo. Dentro [[ ... ]], el patrón global de rhs no se expande de acuerdo con el directorio actual, como lo haría normalmente.
user1934428
@ i336_ No. Dentro [[...]], Bash no realiza la expansión del nombre de archivo. En bash manual,Word splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong
@jinbeomhong: TIL. Eso es bueno saber, gracias!
i336_
2

shopt -s nocasematch

if [[ sed-4.2.2.$LINE =~ (yes|y)$ ]]
 then exit 0 
fi
Shyam Gupta
fuente