8 votes

En bash, y a-t-il un moyen de raccourcir (if ou) les instructions ?

si [ foo = bar -o foo = car -o foo = jar -o foo = foo ]
  then
    echo oui
fi

Pour garder les choses plus organisées, j'aimerais essayer de le faire correspondre à une liste, comme par exemple

if [ foo = {bar,car,jar,foo} ] ; then

Évidemment, la méthode d'expansion des accolades n'a pas fonctionné sinon je ne serais pas ici! Mais j'aimerais savoir si quelque chose comme cela est possible du tout.

15voto

Eliah Kagan Points 111731

Il semble que ce soit un bug dans votre code, mais nous pouvons le refactoriser vraiment petit...

Si vous aviez vraiment écrit votre expression avec [ foo = bar -o foo = car -o foo = jar -o foo = foo ], alors cela aurait pu être simplifié à :

echo oui

Cette condition est toujours vraie car foo = foo est toujours vrai, ce qui est lui-même vrai car la chaîne littérale foo est toujours la même que lui-même.

Bien que vous ayez clarifié que cette syntaxe n'apparaissait pas dans le code que vous utilisiez vraiment, j'ai gardé cette section d'introduction car c'est une erreur courante de le faire, surtout parmi les novices. Essayer de vérifier la valeur de foo de cette manière ne fonctionne pas dans un shell de style Bourne--pour examiner la valeur d'une variable avec [, vous devez effectuer une expansion de paramètres avec $ (voir ci-dessous). Cet aspect particulier du problème n'est pas spécifique à un shell ; par exemple, voici un test dans 7 shells de style Bourne.

Réparation du Code Initial

Peut-être vouliez-vous vérifier la valeur de la variable foo (c'est-à-dire son contenu, plutôt que son nom) contre ces chaînes. Dans ce cas, votre code initial ressemblerait à ceci :

if [ "$foo" = bar -o "$foo" = car -o "$foo" = jar -o "$foo" = foo ]; then
    echo oui
fi

Cela peut être légèrement raccourci avec l'opérateur de court-circuit && :

[ "$foo" = bar -o "$foo" = car -o "$foo" = jar -o "$foo" = foo ] && echo oui

Mais c'est toujours plus long, plus compliqué et plus répétitif que ce que vous préférez probablement. Donc, passons à autre chose...

Formes Plus Courtes via Correspondance de Motifs

Vous pouvez utiliser l'opérateur de correspondance de motif intégré de la facilité [[ :

[[ $foo =~ ^(bar|car|jar|foo)$ ]] && echo oui

Alors que l'utilisation de case est probablement la manière la plus courante, l'utilisation de [[ avec =~ est :

  • de même longueur qu'une utilisation compacte de case (voir ci-dessous)
  • probablement plus fidèle à la logique de votre code initial, dans la mesure où il utilise [[, qui fonctionne comme une expression de test.

Vous pourriez donc préférer cela. C'est la méthode que j'utiliserais probablement pour faire cela en bash.

Si vous vouliez faire quelque chose de similaire mais en utilisant grep au lieu de [[ avec =~, voici une méthode similaire :

grep -Eq '^(bar|car|jar|foo)$' <<< "$foo" && echo oui

Ou cela :

grep -Fqe{bar,car,jar,foo} <<< "$foo" && echo oui

Un One-Liner avec case

Comme le dit Serg, cela peut être simplifié en utilisant case au lieu de if et [. C'est la manière classique et probablement la plus courante.

La méthode dans la réponse de Serg fonctionne parfaitement bien, mais pour le code particulier que vous refactorez, nous pouvons écrire quelque chose de plus simple, en n'utilisant qu'un seul motif pour case. Il n'est pas nécessaire d'avoir une correspondance *) par défaut à la fin. Si la chaîne ne correspond à aucun des motifs, le contrôle passe simplement à travers la construction case et aucune commande n'est exécutée, ce qui est équivalent à votre construction if sans else.

case $foo in bar|car|jar|foo) echo oui;; esac

Cela le rend assez court et simple pour tenir facilement--et être lisible--sur une seule ligne (ce qui semble être ce que vous recherchez). Cette méthode est également portable à des shells de style Bourne autres que bash.

10voto

Sergiy Kolodyazhnyy Points 97292

Je suggère d'utiliser une structure de cas. Quelque chose comme :

case $foo in
    foo|car|bar|jar) echo yes ;;
    *) ;;
esac

Si vous le souhaitez, vous pouvez ajouter une commande entre *) et ;; pour être exécutée lorsque $foo ne correspond pas à foo, car, bar ou jar. Par exemple, vous pourriez afficher un message avec echo 'input incorrect' ou quelque chose comme ça.

3voto

ieplugin Points 216

Encore plus court :

[[ $foo =~ ^([bcj]ar|foo)$ ]] && echo yes

De cette façon $foo est mis en correspondance avec une expression régulière commençant par b, c ou j suivi de ar ou correspondant exactement à foo.

Cela utilise l'opérateur && comme un court-circuit pour exécuter echo yes uniquement si l'expression [[ $foo =~ ^([bcj]ar|foo)$ ]] évalue à vrai.

Si cela est trop compact pour être lu facilement, vous pouvez toujours utiliser la construction complète du if, qui fait exactement la même chose mais est plus lisible :

if [[ $foo =~ ^([bcj]ar|foo)$ ]]
then
    echo yes
fi

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X