utiliser une expression régulière dans if-condition dans bash

87

Je me demande la règle générale pour utiliser une expression régulière dans la clause if dans bash?

Voici un exemple

$ 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  
$   

Pourquoi les trois derniers ne correspondent pas?

J'espère que vous pourriez donner autant de règles générales que possible, pas seulement pour cet exemple.

Tim
la source

Réponses:

127

Lors de l'utilisation d'un modèle global, un point d'interrogation représente un seul caractère et un astérisque représente une séquence de zéro ou plusieurs caractères:

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

Lors de l'utilisation d'une expression régulière, un point représente un seul caractère et un astérisque représente zéro ou plus du caractère précédent. Ainsi " .*" représente zéro ou plus de n'importe quel caractère, " a*" représente zéro ou plus "a", " [0-9]*" représente zéro ou plusieurs chiffres. Un autre signe utile (parmi tant d'autres) est le signe plus qui représente un ou plusieurs des caractères précédents. Ainsi " [a-z]+" représente un ou plusieurs caractères alpha minuscules (dans les paramètres régionaux C - et quelques autres).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi
Suspendu jusqu'à nouvel ordre.
la source
Il y a donc deux façons de faire correspondre une chaîne: le modèle glob et l'expression régulière? Le glob pettern n'est-il pas seulement utilisé pour les noms de fichiers? Dans bash, quand utiliser le modèle glob et quand utiliser une expression régulière? Merci!
Tim
1
@Tim: Globbing est disponible dans la plupart ou toutes les versions de Bash. La correspondance Regex n'est disponible que dans la version 3 et supérieure, mais je recommanderais de ne l'utiliser que dans la version 3.2 et ultérieure. Les expressions régulières sont beaucoup plus polyvalentes que le globbing.
Suspendu jusqu'à nouvel ordre.
13
if [[ $gg =~ ^....grid.* ]]
Ignacio Vazquez-Abrams
la source
1
Vous devriez pouvoir utiliser ". {4}" au lieu de "....", c'est-à-dire "^. {4} grid. *". Cela peut être plus facile à lire et à comprendre.
user276648
7

Ajout de cette solution avec grepet des fonctions shintégrées de base pour ceux qui sont intéressés par une solution plus portable (indépendante de la bashversion; fonctionne également avec des plates sh-formes non-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    

Certaines grepincarnations prennent également en charge l' -qoption (calme) comme alternative à la redirection vers /dev/null, mais la redirection est à nouveau la plus portable.

vladr
la source
j'ai oublié un ")" de fermeture pour egrep
ghostdog74
5
Utilisez à la grep -qplace de grep >/dev/null.
bfontaine
3

@OP,

Le glob pettern n'est-il pas seulement utilisé pour les noms de fichiers?

Non, le modèle "glob" n'est pas seulement utilisé pour les noms de fichiers. vous pouvez également l'utiliser pour comparer des chaînes. Dans vos exemples, vous pouvez utiliser case / esac pour rechercher des modèles de chaînes.

 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

Dans bash, quand utiliser le modèle glob et quand utiliser une expression régulière? Merci!

Les expressions régulières sont plus polyvalentes et "pratiques" que les "modèles globaux", mais à moins que vous n'effectuiez des tâches complexes que le "globbing / globbing étendu" ne peut pas fournir facilement, alors il n'est pas nécessaire d'utiliser des expressions régulières. Les expressions régulières ne sont pas prises en charge pour la version de bash <3.2 (comme dennis l'a mentionné), mais vous pouvez toujours utiliser le globbing étendu (en définissant extglob). pour le globbing étendu, voir ici et quelques exemples simples ici .

Mise à jour pour OP: Exemple de recherche de fichiers commençant par 2 caractères (les points "." Signifie 1 caractère) suivis de "g" en utilisant regex

par exemple sortie

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

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

Dans ce qui précède, les fichiers sont mis en correspondance car leurs noms contiennent 2 caractères suivis de "g". (c'est à dire..g ).

L'équivalent avec le globbing sera quelque chose comme ceci: (regardez la référence pour la signification de ?et *)

$ for file in ??g*; do echo $file; done
abg
degree
..g
ghostdog74
la source
Merci ghostdog74. Dans Bash avec une version supérieure à 3.2, une expression régulière peut-elle être utilisée pour remplacer le motif glob partout où ce dernier apparaît? Ou l'expression régulière ne peut être utilisée que dans certaines circonstances particulières? Par exemple, j'ai trouvé que "ls ?? g" fonctionne alors que "ls ..g" ne l'est pas.
Tim
Rien ne vous empêche d'utiliser regex s'il y a lieu. C'est à vous. Notez que la syntaxe regex est différente de la syntaxe de globbing shell. donc ls ..gne fonctionne pas. Vous dites au shell de rechercher un fichier nommé ..g. En ce qui concerne l' apprentissage de la syntaxe des expressions régulières, vous pouvez essayer perldoc perlretut, perldoc perlrequickou faire info sedsur la ligne de commande.
ghostdog74