Aide-mémoire Unix : Shell-scripts


Sont encore en les parties de ce doc. relatives au C-shell

Table des matières de chapitre

  1. Généralités
  2. Prologue .cshrc
  3. Debugging d'un script
  4. Passage d'arguments dans un script
  5. Interaction d'un script avec l'utilisateur
  6. Appel d'un autre script
  7. Structure "if"
  8. Commande "test"
  9. Structure "case"
  10. Boucles "for"
  11. Boucles "while" et "until"
  12. Divers Bourne-shell
  13. Divers C-shell


1. Généralités

Un shell-script est un fichier contenant une séquence de commandes Unix (procédure de commande). On peut aussi lui passer des arguments, créer et faire usage de variables, réaliser des tests, des branchements, des boucles, des entrées/sorties, etc... Le shell est ainsi utilisé comme un véritable langage de programmation dont les scripts sont interprétés à l'exécution (pas de compilation préalable).

Pour exécuter un script, on a les deux possibilités suivantes :

  1. Dans les deux cas ci-dessous, le script s'exécute dans le shell courant
  2. Dans ce cas, par contre, le script s'exécute dans un shell-fils (et le prologue ~/.cshrc est au préalable exécuté). Inconvénient : l'environnement du shell principal n'est pas modifié par ce script. Si le script défini des alias, des variables d'environnement, etc... que l'on veut récupérer dans le shell principal, il ne faut donc pas le lancer de cette façon !
On spécifie le shell qui doit être utilisé pour interpréter/exécuter le script dans la première ligne du script : On recommande aux utilisateurs d'écrire leurs scripts en Bourne-shell. On déconseille en effet le C-shell en raison de l'implémentation relativement différente de celui-ci sur chaque plateforme et de ses bugs (occasionnant des difficultés de portabilité). Exception : le prologue utilisateur du C-shell ou T-shell (fichier ~/.cshrc) qui doit être écrit en C-shell. Le document pointé par ce lien vous en dira plus pourquoi il est déconseillé d'écrire des scripts en C-shell.

Le Bourne-shell possède des commandes internes que le C-shell ne possède pas, et vice-versa. Le prologue utilisateur du Bourne-shell est le fichier ~/.profile

Les commentaires, dans un shell-script, doivent être précédés du caractère #


2. Prologue .cshrc

Ce script ~/.cshrc, qui se trouve dans votre répertoire principal et vous appartient, est automatiquement exécuté chaque fois qu'un C-shell ou T-shell est invoqué (début de session, ouverture fenêtre, etc...). Il doit donc être écrit en C-shell.

C'est dans ce fichier que l'utilisateur peut définir ses propres alias, variables d'environnement...

Il est conseillé de structurer ce script en 2 parties :

  1. les instructions qui doivent s'exécuter dans tous les cas
  2. celles qui ne doivent être exécutées que pour un shell interactif
Exemple :
      #!/bin/csh
           ...
           partie du script s'exécutant dans tous les cas
           ...
      if ($?USER == 0 || $?prompt == 0) exit  # sortie si Shell non interactif
           ...
           partie du script ne s'exécutant que si shell interactif
           ...
Ne mettez pas de commandes interactives (demandant une intervention de l'utilisateur) dans ce prologue ~/.cshrc

Lorsque l'on vient de modifier son fichier ~/.cshrc, pour bénéficier immédiatement des modifications dans la session courante, passer dans chaque fenêtre d'émulation terminal la commande :
"source ~/.cshrc"


3. Debugging d'un script

Bourne-shell

Introduire dans le script l'instruction : Autre méthode : lancer le script dans nouveau Shell avec : "sh -x script"


4. Passage d'arguments dans un script

On peut récupérer dans le script les arguments passés dans la ligne de commande lors de l'appel du script de la façon suivante : Exemple : si le script bonjour contient :
      #!/bin/sh
      echo Hello $1 $2 ! Ca va bien ?
la commande : "bonjour Monsieur Dupond" retourne :
      Hello Monsieur Dupond ! Ca va bien ?
Autres variables


5. Interaction d'un script avec l'utilisateur

Bourne-shell

On utilise la commande "read variable"
Exemple : si le script dir contient :
      #!/bin/sh
      echo "Nom du fichier ? \c"
      # on aurait aussi pu faire :  /usr/bin/echo -n "Nom du fichier ? "
      read fichier
      ls -l $fichier
la commande : "dir" retourne :
      Nom du fichier ? toto
      -rw-rw-r--  1 dupond   83   Jun 6 17:53  toto
Si l'on fait "read var1 var2 var3", read découpe la ligne que l'on a frappée en mots et met le 1er sur var1, le second sur var2, et tout le reste de la ligne sur var3

C-shell

La variable $< attend une valeur de l'entrée standard.
Exemple : si le script dir contient :
      #!/bin/csh
      echo -n "Nom du fichier ? "
      set fichier = $<
      ls -l $fichier
la commande : "dir" retourne :
      Nom du fichier ? toto
      -rw-rw-r--  1 dupond   83   Jun 6 17:53  toto


6. Appel d'un autre script

Pour appeler un autre script :


7. Structure "if"

Bourne-shell

La commande_if est exécutée, et si elle renvoie un code de retour nul ($?=0) les commande(s) qui suivent sont exécutées. Forme générale de la structure if :

      if commande_if
      then commande(s)
    { elif commande_if
      then commande(s)   }
        .
        .
    { else commande(s)   }
      fi
Variante plus compacte (notez le ";") :
      if commande_if ; then
        commande(s)
      fi
On peut bien entendu faire des blocs if imbriqués.

Exemple :

      heure=`date | awk '{print $4}' | awk -F: '{print $1}'`
      if [ $heure -lt 12 ]
      then
        echo "Bonjour !"
      elif [ $heure -lt 17 ]
      then
        echo "Bon après-midi !"
      else
        echo "Bonsoir !"
      fi


8. Commande "test"

Bourne-shell

Cette commande, qui retourne le status 0 si la condition est vraie, est essentiellement utilisée dans les structures if, while et until. Elle existe sous deux formes différentes :

      [ condition ]           ou
      test condition
Dans la première forme, les espaces entre les crochets [ ] et la condition sont absolument nécessaires !

Les conditions peuvent être :
- niées : par l'opérateur logique de négation "!"
- combinées : par les opérateurs logiques "-o" (or) et "-a" (and)

Les conditions les plus utilisées sont :

C-shell

Le test de l'existence d'une variable est $?variable. S'il retourne 0 c'est que la variable n'est pas définie.


9. Structure "case"

Bourne-shell

Forme générale :

      case chaîne in
      motif1) commande(s) ;;
      motif2) commande(s) ;;
        .
        .
      esac
La chaîne testée peut prendre diverses formes : chiffre, lettre, mot... et les motifs peuvent comporter des caractères spéciaux. On peut regrouper plusieurs motifs dans une même alternative en utilisant le caractère "|"

Exemple :

      if [ -z "$1" ] ; then
        echo "Vous n'avez pas passé de paramètre"
        echo "Relancez ce script avec yes, y, oui, o, non, no ou n"
        echo "(minuscules ou majuscules)"
      else
        case $1 in
          y*|o*|Y*|O*) echo "Vous avez choisi OUI" ;;
          n*|N*)       echo "Vous avez choisi NON" ;;
          *)           echo "Vous avez choisi autre chose que OUI ou NON" ;;
        esac
      fi


10. Boucle "for"

Bourne-shell

La boucle for peut prendre 3 formes. Dans chacune d'entre elles, les commandes placées entre do et done sont exécutées pour chaque valeur prise par la variable contrôlée.

1ère forme : variable prend successivement les valeurs chaîne1 chaîne2 ... chaînen

      for variable in chaîne1 chaîne2 ... chaînen ; do
        commande(s)
      done
2ème forme : variable prend ses valeurs dans la liste des paramètres passés à l'appel du script ($1 $2 $3 ...) :
      for variable do
      # équivalent à : for variable in "$*"
        commande(s)
      done
3ème forme : la liste des fichiers du répertoire courant constitue les valeurs prises par variable :
      for variable in * ; do
        commande(s)
      done

Exemple : procédure qui renomme les fichiers de noms majuscules *.PAS en noms minuscules *.p :

      for fichier in *.PAS ; do
          base=`basename $fichier .PAS`
          mv $fichier `echo $base.p | tr '[A-Z]' '[a-z]'`
      done

C-shell

Exemple : procédure qui renomme les fichiers de noms majuscules *.PAS en noms minuscules *.p :
      foreach fichier ( *.PAS )
          set base=`basename $fichier .PAS`
          mv $fichier `echo $base.p | tr '[A-Z]' '[a-z]'`
      end


11. Boucles "while" et "until"

Bourne-shell

Les boucles while et until sont des itérations non bornées.
Les commande(s) sont exécutées : La forme de ces structures est :
      while commande_testée
      do
        commande(s)
      done
et
      until commande_testée
      do
        commande(s)
      done
Exemple :
      i=-5
      while [ $i -le 5 ] ; do
        echo $i
        i=`expr $i + 1`
      done
      #
      until [ $i -lt 0 ] ; do
        echo $i
        i=`expr $i - 1`
      done


12. Divers Bourne-shell

Redirection en Bourne-shell

Le mécanisme de redirection est passablement plus puissant qu'en C-shell. Les canaux standards sont accédés via les descripteurs suivants : Les redirections s'effectuent de la façon suivante : En Bourne-shell, il est possible d'établir avec exec une redirection qui sera effective pour plusieurs commandes successives. Exemple :
      exec > nouveau_fichier    # dès maintenant, la sortie standard des commandes
      #                         # qui suivent est redirigée vers fichier spécifié
      ls
      date
      who
      exec > /dev/tty           # rétablissement de la sortie standard sur écran

Caractères spéciaux

Apostrophe à l'envers (`)
Les commandes se trouvant entre 2 apostrophes à l'envers sont exécutées, et il y a substitition par le résultat de la commande. Exemple : "echo `date`"
Apostrophe simples (')
Tout se qui se trouve entre 2 apostrophes simples est considéré comme un seul argument, y compris caractères espaces. Il n'y a pas d'expansion de métacaractères, ni de variables, ni de commandes
Guillemets (")
Tout se qui se trouve entre 2 guillemets est considéré comme un seul argument, y compris caractères espaces. Il n'y a pas d'expansion de métacaractères, mais les substitutions de variables et de commandes s'effectuent !

Traitement des chaînes

$chaîne1$chaîne2
Concaténation de chaîne1 et chaîne2
`expr length "$chaîne"`
Retourne la longueur de chaîne
`expr substr "$chaîne" position longueur`
Retourne sous-chaîne de longueur spécifiée commençant à position
`expr index "$chaîne" liste_car`
Retourne position de liste_car dans chaîne

Arithmétique entière

result=`expr "$operande1 + $operande2"`
Addition de 2 opérandes. Les espaces de part et d'autre de l'opérateur sont obligatoires.
Les autres opérateurs disponibles sont - * / % (ce dernier retournant le reste d'une division entière)

Commande "trap"

      trap commande n1 {n2 ...}
La commande spécifiée est exécutée lorsque le shell reçoit les signaux n1, n2 .... Si la commande est omise, la commande trap réactive les signaux spécifiés. Sans aucun arguments, la commande trap affiche la liste des commandes associées à chaque numéro de signal

Exemples :

      trap '' 15   # le shell ignore désormais le signal 15
      trap 9 15    # réactive les signaux 9 et 15


13. Divers C-shell

Dans bon nombre de domaines (structures de contrôle, opérations arithmétiques sur variables...) la syntaxe du C-shell est simplement identique à celle du langage C dont il s'inspire (d'où son nom).


© Jean-Daniel BONJOUR, ENAC-IT, EPF-Lausanne. Révision: 19.9.97.