Comment combiner la condition AND et OR dans le script bash pour la condition if?

J’essayais de combiner la logique AND & OR dans un script bash si condition. D’une certaine manière, je n’obtiens pas la sortie souhaitée et il est difficile de résoudre le problème. J’essaie de valider les parameters d’entrée passés à un script shell pour aucun paramètre et le premier paramètre passé est valide ou non.

if [ "$#" -ne 1 ] && ([ "$1" == "ABC" ] || [ "$1" == "DEF" ] || [ "$1" == "GHI" ] || [ "$1" == "JKL" ]) then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" exit 1 fi 

Quelqu’un peut-il indiquer ce qui ne va pas ici?

Le problème immédiat avec votre déclaration est un problème de logique : vous vouliez probablement écrire:

 if [ "$#" -ne 1 ] || ! ([ "$1" = "ABC" ] || [ "$1" = "DEF" ] || [ "$1" = "GHI" ] || [ "$1" = "JKL" ]) then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2 exit 1 fi 

C’est-à-dire: abort, si l’un des arguments est supérieur à 1 ou si l’argument donné n’est PAS égal à l’une des valeurs acceptables.

Notez le ! pour annuler l’expression entre parenthèses et l’utilisation de la forme conforme à POSIX de l’opérateur d’égalité de chaîne, = (plutôt que == ).

Cependant, étant donné que vous utilisez Bash, vous pouvez vous débrouiller avec un opérateur [[ ... ]] conditionnel et un opérateur de correspondance des expressions régulières de Bash, =~ :

 if [[ $# -ne 1 || ! $1 =~ ^(ABC|DEF|GHI|JKL)$ ]] then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2 exit 1 fi 

Si la conformité à POSIX n’est pas requirejse, [[ ... ]] est préférable à [ ... ] pour diverses raisons . En l’espèce, $# et $1 n’ont pas besoin d’être cotés, et || pourrait être utilisé à l’ intérieur du conditionnel.

Notez que =~ tel qu’utilisé ci-dessus fonctionne dans Bash 3.2+, alors que la syntaxe extglob implicite utilisée dans la réponse utile de anubhava nécessite Bash 4.1+;
dans les versions précédentes, vous pouvez cependant activer explicitement (et restaurer sa valeur d’origine après) l’option de shell shopt -s extglob : shopt -s extglob .

BASH permet en fait l’utilisation de glob étendu à l’intérieur de [[ ... ]] et && aussi && intérieur.

Donc, vous pouvez faire:

 if [[ $# -ne 1 && $1 == @(ABC|DEF|GHI|JKL) ]]; then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" exit 1 fi 

Quelques choses:

  • [...] dans bash équivaut à la même commande de test (vérifiez la page de manuel), donc ceux && et || ne sont pas des opérateurs logiques, mais plutôt les équivalents shell
  • Les parenthèses dans le shell POSIX ne sont pas un opérateur de regroupement. Ils fonctionneront ici, mais ils ouvrent un sous-shell, il est préférable d’utiliser les options de test standard de -a et -o (en faisant votre instruction if [ "$#" -ne 1 -a \( "$1" == "ABC" -o "$1" == "DEF" -o "$1" == "GHI" -o "$1" == "JKL" \) ] bien que selon votre logique, vous ayez envie de quelque chose comme if [ "$#" -ne 1 -o \( "$1" != "ABC" -a "$1" != "DEF" -a "$1" != "GHI" -a "$1" != "JKL" \) ] . Vous pouvez probablement obtenir de meilleurs résultats avec une déclaration de case comme suit:

 usage() { echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" } if [ "$#" -ne 1 ] then usage exit 1 fi case "$1" in ABC) echo "Found ABC" ;; DEF) echo "Found DEF" ;; GHI) echo "Found GHI" ;; JKL) echo "Found JKL" ;; *) usage exit 1 ;; esac 

Si vous souhaitez transmettre un ensemble d’arguments statiques possibles, vous pouvez consulter la commande shell spéciale getopts .