216 votes

Comment faire correspondre une chaîne de caractères avec une regex en Bash ?

J'essaie d'écrire un bash script qui contient une fonction de sorte que, lorsqu'on lui donne un .tar , .tar.bz2 , .tar.gz etc., il utilise tar avec les options appropriées pour décompresser le fichier.

J'utilise des instructions if elif then qui testent le nom de fichier pour voir ce qui le termine et je n'arrive pas à le faire correspondre en utilisant des métacaractères regex.

Pour éviter de réécrire constamment le script, j'utilise 'test' à la ligne de commande, j'ai pensé que l'instruction ci-dessous devrait fonctionner, j'ai essayé toutes les combinaisons possibles de parenthèses, de guillemets et de métacaractères et cela échoue toujours.

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

Je suis sûr que le problème est simple et j'ai cherché partout, mais je n'arrive pas à comprendre comment faire. Quelqu'un sait-il comment je peux le faire ?

339voto

dogbane Points 85749

Pour faire correspondre des regex, vous devez utiliser la fonction =~ opérateur.

Essayez ça :

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

Vous pouvez également utiliser des caractères génériques (au lieu de regex) avec la commande == opérateur :

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

Si la portabilité n'est pas un problème, je recommande l'utilisation de [[ au lieu de [ o test car il est plus sûr et plus puissant. Voir [Quelle est la différence entre test, [ et [[ ?](http://mywiki.wooledge.org/BashFAQ/031) pour les détails.

72voto

duality Points 985

Une fonction pour faire cela

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
}

Autre note

En réponse à Aquarius Power dans le commentaire ci-dessus, We need to store the regex on a var

La variable BASH_REMATCH est définie après la correspondance de l'expression, et ${BASH_REMATCH[n]} correspondra au nième groupe entouré de parenthèses, comme dans l'exemple suivant ${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

(L'expression rationnelle ci-dessus n'est pas censée être valide pour les noms et extensions de fichiers, mais elle fonctionne pour l'exemple).

22voto

user2066480 Points 371

Je n'ai pas assez de représentants pour commenter ici, alors je soumets une nouvelle réponse pour améliorer la réponse de dogbane. Le point . dans le regexp

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

correspondra en fait à n'importe quel caractère, et pas seulement au point littéral entre 'tar.bz2', par exemple

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

ou toute autre chose qui ne nécessite pas d'échappement avec '\'. La syntaxe stricte devrait donc être

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

ou vous pouvez être encore plus strict et inclure également le point précédent dans la regex :

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

13voto

user1934428 Points 3811

Puisque vous utilisez bash, vous n'avez pas besoin de créer un processus enfant pour ce faire. Voici une solution qui l'exécute entièrement dans bash :

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

Explication : Les groupes avant et après la séquence "deux points et un ou plusieurs espaces" sont stockés par l'opérateur de correspondance de motifs dans le tableau BASH_REMATCH.

2voto

Shyam Gupta Points 171

Shopt -s nocasematch

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X