10.2. Choix if

10.2.1. Fonctionnement

La commande interne if implante le choix alternatif.

Syntaxe :

if suite_de_commandes1

  then

    suite_de_commandes2

[ elif suite_de_commandes ; then suite_de_commandes ] ...

[ else suite_de_commandes ]

fi

Le fonctionnement est le suivant : suite_de_commandes1 est exécutée ; si son code de retour est égal à 0, alors suite_de_commandes2 est exécutée sinon c'est la branche elif ou la branche else qui est exécutée, si elle existe.

Exemple : programme shell rm1

#!/bin/bash

if rm $1 2>/dev/null
  then echo $1 a ete supprime
  else echo $1 n\'a pas ete supprime
fi
$ >toto    => création du fichier toto
$
$ rm1 toto
toto a ete supprime
$
$ rm1 toto
toto n'a pas ete supprime
$

Lorsque la commande rm1 toto est exécutée, si le fichier toto est effaçable, le fichier est effectivement supprimé, la commande unix rm renvoie un code de retour égal à 0 et c’est la suite de commandes qui suit le mot-clé then qui est exécutée ; le message toto a ete supprime est affiché sur la sortie standard.

Si toto n’est pas effaçable, l’exécution de la commande rm échoue ; celle-ci affiche un message sur la sortie standard pour les messages d’erreur que l’utilisateur ne voit pas car celle-ci a été redirigée vers le puits, puis renvoie un code de retour différent de 0. C’est la suite de commandes qui suit else qui est exécutée : le message toto n’a pas ete supprime s’affiche sur la sortie standard.

Les mots if, then, else, elif et fi sont des mots-clé. Par conséquent, pour indenter une structure if suivant le « style langage C », on pourra l’écrire de la manière suivante :

if suite_de_commandes1 ; then

  suite_de_commandes2

else

  suite_de_commandes

fi

La structure de contrôle doit comporter autant de mots-clés fi que de if (une branche elif ne doit pas se terminer par un fi) :

if ...

  then ...

  elif ...

    then ...

fi


if ...

  then ...

  else if ...

          then ...

        fi

fi

Dans une succession de if imbriqués où le nombre de else est inférieur au nombre de then, le mot-clé fi précise l’association entre les else et les if :


if ...
  then ...
    if ...
      then ...
    fi
  else ...
fi

10.2.2. Commande composée [[

La commande interne composée [[ est souvent utilisée avec la commande interne composée if. Elle permet l’évaluation d’expressions conditionnelles portant sur des objets aussi différents que les permissions sur une entrée, la valeur d’une chaîne de caractères ou encore l’état d’une option de la commande interne set.

Syntaxe : [[ expr_cond ]]

Les deux caractères crochets doivent être collés et un caractère séparateur doit être présent de part et d’autre de expr_cond. Les mots [[ et ]] sont des mots-clé.

Le fonctionnement de cette commande interne est le suivant : l’expression conditionnelle expr_cond est évaluée et si sa valeur est Vrai, alors le code de retour de la commande interne [[ est égal à 0. Si sa valeur est Faux, le code de retour est égal à 1. Si expr_cond est mal formée ou si les caractères crochets ne sont pas collés, une valeur différente est retournée.

La commande interne [[ offre de nombreuses expressions conditionnelles ; c’est pourquoi, seules les principales formes de expr_cond seront présentées, regroupées par catégories.

  • Permissions :

    • -r entrée vraie si entrée existe et est accessible en lecture par le processus courant.

    • -w entrée vraie si entrée existe et est accessible en écriture par le processus courant.

    • -x entrée vraie si le fichier entrée existe et est exécutable par le processus courant ou si le répertoire entrée existe et le processus courant possède la permission de passage.

    $ echo coucou > toto
    $ chmod 200 toto
    $ ls -l toto
    --w------- 1 sanchis sanchis 7 avril  1 09:14 toto
    $
    $ if [[ -r toto ]]
    > then cat toto
    > fi
    $    => aucun affichage donc toto n’existe pas ou n’est pas accessible en lecture,
    $    => dans le cas présent, il est non lisible
    $
    $ echo $?
    0    => code de retour de la commande interne if
    $

    Mais,

    $ [[ -r toto ]]
    $
    $ echo $?
    1    => code de retour de la commande interne [[
    $

  • Types d'une entrée :

    • -f entrée vraie si entrée existe et est un fichier ordinaire.

    • -d entrée vraie si entrée existe et est un répertoire.

    Exemple : programme shell affic

    #!/bin/bash
    
    if [[ -f "$1" ]]
      then 
        echo "$1" : fichier ordinaire
        cat "$1"
      elif [[ -d "$1" ]]
        then 
          echo "$1" : repertoire
          ls "$1"
        else 
          echo "$1" : type non traite
    fi
    
    $ affic .		=> traitement du répertoire courant
    . : repertoire
    affic  err  Exercices  for_liste  for_set  rm1	testval  
    $
  • Renseignements divers sur une entrée :

    • -a entrée vraie si entrée existe.

    • -s entrée vraie si entrée existe et sa taille est différente de 0 (rq : la taille d’un répertoire vide est toujours différente de 0).

    • entrée1 -nt entrée2 vraie si entrée1 existe et sa date de modification est plus récente que celle de entrée2.

    • entrée1 -ot entrée2 vraie si entrée1 existe et est plus ancienne que celle de entrée2.

    $ > err		=> création d’un fichier err vide
    $
    $ ls -l err
    -rw-rw-r-- 1 sanchis sanchis 0 avril  1 08:35 err
    $	 
    $ if [[ -a err ]]
    > then echo err existe
    > fi
    err existe
    $ 
    $ if [[ -s err ]]
    > then echo le contenu du fichier err est non vide
    > else echo son contenu est vide
    > fi
    son contenu est vide
    $

  • Longueur d’une chaîne de caractères :

    • -z ch vraie si la longueur de la chaîne ch est égale à zéro.

    • ch

      -n ch

      Ces deux expressions sont vraies si la longueur de la chaîne ch est différente de zéro.

    $ a=		=> la variable a est définie et est vide
    $ 
    $ if [[ -z $a ]]
    > then echo la longueur de a est egale a 0
    > fi
    la longueur de a est egale a 0
    $ 
    $ echo $#
    0		=> aucun paramètre de position n’est initialisé
    $ 
    $
    $ if [[ -z $1 ]]
    > then echo la longueur est egale a 0
    > fi
    la longueur est egale a 0
    $

  • Comparaisons de chaînes de caractères :

    • ch1 < ch2 vraie si ch1 précède ch2.

    • ch1 > ch2 vraie si ch1 suit ch2.

    L’ordre des chaînes ch1 et ch2 est commandé par la valeur des paramètres régionaux.

    $ a=bonjour A=Bonjour
    $ 
    $ if [[ $a < $A ]]
    > then echo $a precede $A
    > elif [[ $a > $A ]]
    > then echo $a suit $A
    > else echo elles sont egales
    > fi
    bonjour precede Bonjour
    $
    • ch == mod vraie si la chaîne ch correspond au modèle mod.

    • ch != mod vraie si la chaîne ch ne correspond pas au modèle mod.

    mod est un modèle de chaînes pouvant contenir caractères et expressions génériques. Ces derniers ne doivent pas être protégés de l’interprétation de bash lorsqu’ils sont placés entre les caractères [[ ]].

    $ a="au revoir"
    $
    $ [[ $a == 123 ]]		=> faux
    $
    $ echo $?
    1
    $
    $ [[ $a == a* ]]		=> vrai : la valeur de a commence par le caractère a ; on
    $				=>      peut remarquer la non protection du caractère *
    $ echo $?
    0
    $
    $ b=123
    $ 
    $ if [[ $b == +([[:digit:]]) ]] 
    > then
    >   echo "c'est une suite de chiffres"
    > fi
    c'est une suite de chiffres
    $
    $ c=BonJOuR
    $ 
    $ shopt -s nocasematch		=> minuscules et majuscules ne sont pas distinguées
    $ 				 
    $ [[ $c == bonjour ]]
    $ 
    $ echo $?
    0		=> la valeur de la variable c correspond au modèle bonjour
    $

    Si ch ou mod ne sont pas définies, la commande interne [[ ne provoque pas d’erreur de syntaxe.

    Exemple : programme shell testval

    #!/bin/bash
    
    if [[ $1 == $a ]]
      then echo OUI
      else echo >&2 NON
    fi
    
    $ testval coucou
    NON			=> dans testval, $1 est remplacé par coucou, la variable a n’est 
    $			=> 	pas définie
    $ testval
    OUI			=> aucun des deux membres de l’égalité n’est défini
    $
    $ a=bonjour testval bonjour	=> la variable a est locale au fichier shell testval,
    OUI			=> elle est initialisée lors de l’appel du fichier shell
    $

    Remarque : il existe un opérateur =~ qui permet de mettre en correspondance une chaîne de caractère ch avec une expression régulière expr_reg : ch =~ expr_reg

    Contrairement aux caractères et expressions génériques, les expressions régulières ne sont pas spécifiques à bash.

  • Etat d’une option :

    • -o opt vraie si l’état de l’option opt de la commande interne set est à on.

    $ set -o | grep noglob
    noglob off
    $
    $ if [[ -o noglob ]]; then echo ON
    > else echo OFF
    > fi
    OFF
    $

  • Composition d'expressions conditionnelles :

    • ( expr_cond ) vraie si expr_cond est vraie. Permet le regroupement d’expressions conditionnelles.

    • ! expr_cond vraie si expr_cond est fausse.

    • expr_cond1 && expr_cond2 vraie si les deux expr_cond sont vraies. L’expression expr_cond2 n’est pas évaluée si expr_cond1 est fausse.

    • expr_cond1 || expr_cond2 vraie si une des deux expr_cond est vraie. L’expression expr_cond2 n’est pas évaluée si expr_cond1 est vraie.

    Les quatre opérateurs ci-dessus ont été listés par ordre de priorité décroissante.

    $ ls -l /etc/at.deny
    -rw-r----- 1 root daemon 144 oct.  25  2011 /etc/at.deny
    $
    $ if [[ ! ( -w /etc/at.deny || -r /etc/at.deny ) ]]
    > then
    >   echo OUI
    > else
    >   echo NON
    > fi
    OUI
    $

    Le fichier /etc/at.deny n’est accessible ni en lecture ni en écriture pour l’utilisateur sanchis ; le code de retour de la commande interne [[ sera égal à 0 car l’expression conditionnelle globale est vraie.

    Attention : il ne faut pas confondre les opérateurs ! && || utilisés par la commande interne [[ pour construire les expressions conditionnelles et ces mêmes opérateurs ! && || portant sur les codes de retour [cf. Chapitre 7, Code de retour].

    En combinant commande interne [[, opérateurs sur les codes de retour et regroupements de commandes, l’utilisation d’une structure if devient inutile.

    $ ls -l toto
    --w------- 1 sanchis sanchis 7 mai   28 14:26 toto
    $
    $ [[ -r toto ]] || {
    >   echo >&2 "Probleme de lecture sur toto"
    > }
    Probleme de lecture sur toto
    $

    Remarque : par souci de portabilité, bash intègre également l’ancienne commande interne [. Celle-ci possède des fonctionnalités similaires à celles de [[ mais est plus délicate à utiliser.

    $ a="au revoir"
    $
    $ [ $a = coucou ]    => l’opérateur égalité de [ est le symbole =
    bash: [: trop d'arguments
    $

    Le caractère espace présent dans la valeur de la variable a provoque une erreur de syntaxe. Il est nécessaire de prendre davantage de précaution quand on utilise cette commande interne.

    $ [ "$a" = coucou ]
    $
    $ echo $?
    1
    $