Introduction aux scripts BASH


Instructions test if case for while select

La programmation shell

Saisie du script

Exécution du script

  • Mise au point, débogage
    Exécution en mode "trace" (-x) et en mode "verbeux" (-v) sh -x ./bonjour Pour aider à la mise au point d'un script, on peut insérer des lignes temporaires : echo $var pour afficher la valeur de la variable
    exit 1 pour forcer l'arrêt du script à cet endroit

  • On peut passer des arguments à la suite du nom du script, séparés par des espaces. Les valeurs de ces paramètres sont récupérables dans le script grâce aux paramètres de position $1, $2 .. mais, contrairement aux langages de programmation classiques, ils ne peuvent pas être modifiés.
    Exemple
    #!/bin/bash
    # appel du script : ./bonjour nom prenom
    if [ $# = 2 ]
     then
    echo "Bonjour $2 $1 et bonne journée !"
     else
    echo "Syntaxe : $0 nom prenom"
    fi 
    

    Entrées-sorties

    Ce sont les voies de communication entre le programme bash et la console :

    Les variables BASH

    Variables programmeur

    De façon générale, elles sont de type texte. On distingue les variables définies par le programmeur et les variables systèmes

    Variables d'environnement

    Ce sont les variables systèmes dont la liste est consultable par la commande env | less
    Les plus utiles sont $HOME, $PATH, $USER, $PS1, $SHELL, $ENV, $PWD ..



       Exemple (bien sûr on est pas forcément connecté sous le pseudo toto)
    [toto@pxx toto]$ moi=Toto
    [toto@pxx toto]$ p="Je m'appelle $moi"
    [toto@pxx toto]$ echo Aujourd\'hui, quel jour sommes nous ? ; read jour
    [toto@pxx toto]echo aujourd'hui $jour, $p sous le nom $USER,
    			est connecté à la station $HOSTNAME
    


    Variables prédéfinies spéciales

    Elles sont gérées par le système et s'avèrent très utiles dans les scripts. Bien entendu, elles ne sont accessibles qu'en lecture.

    Ces variables sont automatiquement affectées lors d'un appel de script suivi d'une liste de paramètres. Leurs valeurs sont récupérables dans $1, $2 ...$9

     $? C'est la valeur de sortie de la dernière commande.
    Elle vaut 0 si la commande s'est déroulée sans pb.
     $0 Cette variable contient le nom du script
     $1 à $9 Les (éventuels) premiers arguments passés à l'appel du script
     $# Le nombre d'arguments passés au script 
     $* La liste des arguments à partir de $1
     $$ le n° PID du processus courant
     $! le n° PID du processus fils

    ls -l
    echo $?        ----> 0
    ifconfig ttyS1
    echo $?         ---> 1

    Passage de paramétres

    On peut récupérer facilement les compléments de commande passés sous forme d'arguments sur la ligne de commande, à la suite du nom du script, et les utiliser pour effectuer des traitements.
    Ce sont les variables système spéciales $1 , $2 .... $9 appelées paramètres de position.
    Celles-ci prennent au moment de l'appel du script, les valeurs des chaines passées à la suite du nom du script (le séparateur de mot est l'espace, donc utiliser si nécessaire des "").
    A noter que :

    La commande shift

    La commande set


    La commande test

    Généralités

    Comme son nom l'indique, elle sert à vérifier des conditions. Ces conditions portent sur des fichiers (le plus souvent), ou des chaines ou une expression numérique.
    Cette commande courante sert donc à prendre des (bonnes) décisions, d'où son utilisation comme condition dans les structures conditionnelles if.. then ..else, en quelque sorte à la place de variables booléennes ... qui n'existent pas.

    Syntaxe

    Valeur de retour

    Tester un fichier

    Tester une chaine

    Tester un nombre

    Opérations dans une commande test


    Structures conditionnelles

    if suite-de-commandes
    then
    # séquence exécutée si suite-de-commandes rend une valeur 0
     bloc-instruction1
    else
    # séquence exécutée sinon 
     bloc-instruction2
    fi
    
    Attention ! si then est placé sur la 1ère ligne, séparer avec un ;
    if commande; then
     .....


    Exemples

    1. toto posséde t-il un compte ? On teste la présence d'une ligne commençant par toto dans /etc/passwd ( >/dev/null pour détourner l'affichage de la ligne trouvée)
      if grep "^toto" /etc/passwd > /dev/null
      then 
       echo "Toto a déjà un compte"
      fi
      
    2. Si toto a eu une bonne note, on le félicite
      note=17
      if [ $note -gt 16 ] ---> test vrai, valeur retournée : 0
      then echo "Très bien !"
      fi
      
    3. Avant d'exécuter un script, tester son existence.
      Extrait de $HOME/.bash_profile
      if [ -f ~/.bashrc ]
      then
       .~/.bashrc
      fi
      

    Conditionnelles imbriquées
    Pour imbriquer plusieurs conditions, on utilise la construction :

    if commande1
    then
     bloc-instruction1
    elif commande2
    then
     bloc-instruction2
    else
    # si toutes les conditions précédentes sont fausses
     bloc-instruction3
    fi
    

    Exemples

    1. toto a t-il fait son devoir lisiblement ?
      fichier=/home/toto/devoir1.html
      if [ -f $fichier -a -r $fichier ]
      then
      echo "je vais vérifier ton devoir."
      elif [ ! -e $fichier ]
       then
        echo "ton devoir n'existe pas !"
       else
        echo "je ne peux pas le lire !"
      fi
      
    2. Supposons que le script exige la présence d'au moins un paramètre, il faut tester la valeur de $#, est-elle nulle ?
      if [ $# = 0 ]
      then
      echo "Erreur, la commande exige au moins un argument .."
       exit 1
      elif [ $# = 1 ]
       then 
        echo "Donner le second argument : "
       read arg2
      fi
      


    Choix multiples

    case valeur in
     expr1) commandes ;;
     expr2) commandes ;;
     ...
    esac


    Exemples

    1. Supposons que le script doive réagir différemment selon l'user courant; on va faire plusieurs cas selon la valeur de $USER

      case $USER in
        root) echo "Mes respects M le $USER" ;;
        jean | stage?) echo "Salut à $USER ;;
        toto) echo "Fais pas le zigo$USER \!" ;;
      esac
      
    2. Le script attend une réponse oui/non de l'utilisateur
      read reponse
      case $reponse in
        [yYoO]*) ...... ;;
        [nN]*)  .......;;
      esac
    3. read langue
      case $langue in
      francais) echo Bonjour ;;
      anglais) echo Hello ;;
      espagnol) echo Buenos Dias ;;
      esac
    4. case $param in
      0|1|2|3|4|5|6|7|8|9 ) echo $param est un chiffre ;;
      [0-9]*) echo $param est un nombre ;;
      [a-zA-Z]*) echo $param est un nom ;;
      *) echo $param de type non prevu ;;
      esac
      
    5. Un vrai exemple, extrait du script smb (/etc/rc.d/init.d/smb)
      # smb attend un paramètre, récupéré dans la variable $1
      case "$1" in 
        start)
            echo -n "Starting SMB services: "
          deamon smbd -D
          echo
          echo -n "Starting NMB services: "
          deamon nmbd -D
          ...;;
        stop)
             echo -n "Shutting SMB services: "
          killproc smbd 
          ....
      esac    
      

    Structures itératives

    Boucle for

    Boucle while

    while liste-commandes
    do
     commandes
    done
    La répétition se poursuit TANT QUE la dernière commande de la liste est vraie (c-à-dire renvoie un code de retour nul) Voici 2 exemples à comparer
    echo -e "Entrez un nom de fichier"
    read fich
    while [ -z "$fich" ]
    do 
    echo -e "Saisie à recommencer"
    read fich
    done
    while 
    echo -e" Entrez un nom de fichier"
    read fich
    [ -z "$fich" ]
    do 
    echo -e "Saisie à recommencer" 
    done
    until liste-commandes
    do
     commandes
    done
    La répétition se poursuit JUSQU'A CE QUE la dernière commande de la liste devienne vraie

    Exemples à tester

    # Pour dire bonjour toutes les secondes (arrêt par CTRL-C)
    while true ; 
    do 
    echo "Bonjour M. $USER"
    sleep 1 
    done
    
    Lecture des lignes d'un fichier pour traitement : noter que la redirection de l'entrée de la commande while .. do .. done est placée à la fin
    fich=/etc/passwd
    while read ligne
    do
     echo $ligne
     .......
    done < $fich
    

    Sortie et reprise de boucle


    break placé dans le corps d'une boucle, provoque une sortie définitive cette boucle.

    continue permet de sauter les instructions du corps de la boucle (qui suivent continue) et de "continuer" à l'itération suivante.
    Pour les boucles for, while et until, continue provoque donc la réévaluation immédiate du test de la boucle.

    Exemples importants
    Boucle de lecture au clavier arrêtée par la saisie de stop

    #!/bin/bash
    # syntaxe : lecture.sh
    texte=""
    while true
    do
     read ligne
     if [ $ligne = stop ]
     then break
     else texte="$texte \n$ligne"
     fi
    done
    echo -e $texte
    
    Lecture des lignes d'un fichier
    fich="/etc/passwd"
    grep "^stage" $fich | while true
    do
     read ligne
     if [ "$ligne" = "" ] ; then break ; fi 
     echo $ligne
    done
    

    Fonctions

    Commandes diverses

    Calcul sur les entiers relatifs

    Ne pas confondre la syntaxe $((expresion arithmétique)) avec la substitution de commande $(commande)
    Les priorités sont gérées par un parenthèsage habituel
    echo $((30+2*10/4))
    echo $(( (30+2) * (10-7) /4 ))

    tr

    set

    Cette commande interne est très pratique pour séparer une ligne en une liste de mots, chacun de ces mots étant affecté à une variable positionnelle. Le caractère de séparation est l'espace.
    # soit une chaine ch qui contient une liste de mots 
    c="prof eleve classe note"  
    # set  va lire chaque mot de la liste et l'affecter aux paramètres de position
    set  $c  ; echo $1 $2 $3 $4
    shift  ;  echo $1 $2 $3 $4
    
    Le langage bash est inadapté aux calculs numériques. Mais si vraiment on veut calculer (sur des entiers) ..
    Exemple : calcul des premières factorielles (attention, il y a rapidement un dépassement de capacité)
    declare -i k ; k=1 ; p=1
    while [ $k -le 10 ]
    do echo "$k! = " $((p=$p * $k)) ; k= $k+1
    done
    Idée (saugrenue !) : écrire le script somme-entiers.sh pour calculer la somme 1+2+..+n, où la valeur de n est passée en argument

    eval

    Application aux scripts cgi

    La soumission d'un formulaire HTML à la passerelle CGI est un mécanisme qui aboutit à la récupération par le script (Bash, Perl ,etc..) d'une chaine qui contient la requête sous un format particulier.
    Voici un exemple