diff --git a/unix/shell/fonction.tml b/unix/shell/fonction.tml new file mode 100644 index 0000000..df12bd4 --- /dev/null +++ b/unix/shell/fonction.tml @@ -0,0 +1,412 @@ + + + + +Fonctions + + + +

Les fonctions en shell

+ +

+Vous savez qu'un script shell n'est rien d'autre qu'une série de +commandes (si vous ne le savez pas, lisez la page d'initiation à la programmation en shell). Mais +parfois cela pose des problèmes, car lorsqu'un script devient un peu +long et surtout lorsqu'il est obligé de se répéter, les risques de +bogues (dysfonctionnements) croissent. +

+ +

+L'usage des fonctions permet de : +

+ +
    +
  1. éviter ces répétitions ;
  2. +
  3. diminuer les risques de bogues ;
  4. +
  5. augmenter la lisibilité du script pour un humain.
  6. +
+ + +

Pourquoi des fonctions ?

+ +

Un programme sans fonction

+ +

+Utilisateur de TeX, vous voulez effacer régulièrement tous les fichiers +.aux et .log qui polluent vos répertoires. +Pour ceux qui ne connaissent pas TeX, sachez que ce sont des fichiers +construits automatiquement, et que l'on peut recréer facilement à partir +du fichier .tex : les supprimer permet donc de gagner +de l'espace disque sans dommages. +

+ +

+Votre script ressemble donc à ceci : +

+ +
+#!/bin/sh
+
+# fichier "texcleaner" : efface les fichiers aux et log
+
+# Je prends chaque fichier .aux du répertoire courant
+for fichier in *.aux 
+  do 
+     # J'affiche son nom et demande confirmation pour l'effacer
+     echo "$fichier"
+     echo "Voulez-vous vraiment l'effacer ? (o/n)"
+
+     # Je lis la réponse de l'utilisateur
+     read reponse
+
+     # Et s'il dit oui, j'efface
+     if [[ $reponse == "o" ]]
+       then rm -f $fichier
+     fi  
+done
+
+# Je prends chaque fichier .log du répertoire courant
+for fichier in *.log
+  do 
+     # J'affiche son nom et demande confirmation pour l'effacer
+     echo "$fichier"
+     echo "Voulez-vous vraiment l'effacer ? (o/n)"
+
+     # Je lis la réponse de l'utilisateur
+     read reponse
+
+     # Et s'il dit oui, j'efface
+     if [[ $reponse == "o" ]]
+       then rm -f $fichier
+     fi
+done
+
+ +

+Vous venez de terminer ce programme, et vous êtes content, car il +fonctionne comme vous le voulez. +

+ + +

Problèmes des programmes sans fonction

+ +

+Ce programme présente certains aspects déplaisants : +

+ +
    +
  1. il n'est pas très lisible ;
  2. +
  3. il comporte des répétitions relativement longues (une dizaine de +lignes) ;
  4. +
  5. si vous voulez changer le moindre détail, vous devrez rechercher à +la main toutes les occurrences de ce que vous voulez changer, ce +qui : +
      +
    1. est fatiguant ;
    2. +
    3. est fastidieux ;
    4. +
    5. est, surtout, très peu fiable : si vous +oubliez une occurrence ou que vous modifiez un endroit alors qu'il ne le +fallait pas, les conséquences sont parfois graves.
    6. +
    +
  6. +
+ +

+Il faudrait, pour pallier ces inconvénients, trouver un moyen de +centraliser les informations destinées à être répétées, afin que +l'on puisse s'y référer à chaque endroit où cela est +nécessaire. C'est pour cela que les fonctions existent. +

+ + + +

Définition et appel d'une fonction

+ +

+L'utilisation des fonctions se fait en deux moments : +

+ +
    +
  1. d'abord, il faut définir la fonction : vous +décrivez quelle série de commandes il faudra exécuter lrosque l'on +appellera la fonction ;
  2. + +
  3. ensuite, il faut appeler la fonction à chaque +endroit que vous voulez.
  4. +
+ + +

Analogies avec le monde humain

+ +

+Dans le monde naturel, on peut comparer cela à l'horloge parlante : +celle-ci commence par dire « au quatrième top, il sera neuf heures +cinquante-deux », puis « top... top... top... top. » +

+ +

+On pourrait dire que dans un premier temps, l'horloge parlante +définit une fonction, en assignant un signal (le quatrième top) +à ce qu'il signale (il est neuf heures cinquante-deux) ; une fois +cela clairement défini, l'horloge égrène les quatre +« top », et le quatrième renvoie à l'énoncé « il est +neuf heures cinquante-deux ». +

+ +

+En langage naturel, définir une fonction équivaut à dire : +quand je dirai le nom de la fonction, vous l'exécuterez. C'est +un acte de langage, comme quand un arbitre dit aux athlètes : +« Go ! » ; car tous les athlètes savent que le +signifiant « Go ! » (ou le coup de pistolet tiré en +l'air) signifie qu'ils doivent partir (la fonction a été définie dans +le règlement officiel de leur sport). +

+ + +

Définir une fonction

+ +

Comment définir les fonctions ?

+ +

+Définir une fonction est extrêmement simple : +

+ +
+nom () {
+instruction1
+instruction2
+...
+}
+
+ +

+On commence par trouver un nom à la fonction. Vous +pouvez choisir ce nom à votre guise, par exemple liste_des_fichiers, +effacer_fichier, etc. +

+ +

+Il est toutefois fortement recommandé : +

+ + + +

+Une fois que vous avez donné un nom à la fonction, notez des +parenthèses ouvrante et fermante : ce sont elles +qui indiquent à l'interpréteur du script qu'il s'agit d'une définition +de fonction. +

+ +

+Ensuite, entre accolades, notez la série des +instructions qu'il faudra exécuter à chaque appel de la +fonction. +

+ + +

Où définir les fonctions ?

+ +

+Comme l'interpréteur de scripts shell lit des scripts ligne à ligne, +il faut que la fonction soit définie avant d'être +appelée. Sinon, vous recevez un message de type : +« Command not found » (commande introuvable). Par convention, +il est préférable de placer toutes les définitions de +fonction vers le début du programme, avant toutes les +instructions. +

+ + +

Appeler une fonction

+ +

+Notre programme sera donc considérablement allégé : +

+ +
+#!/bin/sh
+
+# fichier "texcleaner" : efface les fichiers aux et log
+
+# Je définis ma fonction effacer_fichier
+
+effacer_fichier () {
+     # J'affiche son nom et demande confirmation pour l'effacer
+     echo "$1"
+     echo "Voulez-vous vraiment l'effacer ? (o/n)"
+
+     # Je lis la réponse de l'utilisateur
+     read reponse
+
+     # Et s'il dit oui, j'efface
+     if [[ $reponse == "o" ]]
+       then rm -f $1
+     fi  
+}
+
+# Je prends chaque fichier .aux du répertoire courant
+for fichier in *.aux 
+  do 
+    # J'appelle la fonction effacer_fichier pour chaque fichier
+    effacer_fichier $fichier
+done
+
+# Je prends chaque fichier .log du répertoire courant
+for fichier in *.log
+  do 
+    # J'appelle la fonction effacer_fichier pour chaque fichier
+    effacer_fichier $fichier
+done
+
+ +

+On économise une dizaine de lignes, on gagne en lisibilité, et les +risques de bogues diminuent considérablement car s'il y a des +corrections à apporter, c'est à un seul endroit du script, et non d'une +façon disséminée sur l'ensemble du programme. +

+ +

+Un détail de la fonction effacer_fichier peut vous +étonner : nous utilisons l'argument $1. Comme vous le +savez sans doute (sinon, lisez la page sur les commandes +shell et leurs arguments), $1 désigne le premier +argument passé à une commande ; or les fonctions peuvent +recevoir des arguments, exactement de la même façon que les +commandes. C'est même tout à fait normal, car les fonctions sont +en fait des commandes comme les autres, à ceci près qu'elles ne sont +valables qu'à l'échelle d'un script, et non à celle d'un +système tout entier. +

+ + +

Les appels entre fonctions

+ +

+Sachez enfin que des fonctions peuvent appeler d'autres +fonctions, ce qui donne une extrême souplesse à leur +utilisation. +

+ +

+En voici un bref exemple : +

+ +
+#!/bin/sh
+
+# Je définis une première fonction
+
+ecrire_sur_une_ligne () {
+  echo -n $*
+}
+
+# Je définis une deuxième fonction qui appelle la première
+
+saluer_utilisateur () {
+  ecrire_sur_une_ligne "Bonjour "
+  echo $USER
+}
+
+
+# J'appelle la deuxième fonction
+saluer_utilisateur
+
+
+ + +

Abusez des fonctions

+ +

+De là, passons à un conseil de programmation : abusez des +fonctions ! N'hésitez pas à créer des fonctions pour tout +et n'importe quoi. Vous en tirerez : +

+ +
    +
  1. un gain de lisibilité ;
  2. +
  3. un gain d'efficacité ;
  4. +
  5. un gain de souplesse.
  6. +
+ +

Gain de lisibilité

+ +

+Un programme sans fonction n'est lisible que s'il est très +petit : une vingtaine de lignes tout au plus. Dès qu'un +programme dépasse cette taille, il cesse d'être intelligible d'un seul +coup d'œil pour un humain. +

+ +

+Supposons qu'un programme soit amené à répéter n fois un +même fragment de code comportant p lignes ; en +utilisant des fonctions on économise +(n - 1) x p  lignes (toutes +les occurrences de la répétition, moins la définition de la fonction). +

+ +

+Ce gain de lignes est indissociable d'un gain de lisibilité, car les +répétitions fatiguent inutilement le cerveau humain. +

+ + +

Gain d'efficacité

+ +

+En recopiant « à la main » des fragments identiques de codes, +vous risquez toujours la faute de frappe. Or, la moindre coquille peut +avoir des conséquences, au mieux, imprévisibles ; au pire, +catastrophiques. +

+ +

+En isolant les séries d'instructions dans des définitions de fonctions, +vous concentrez en un seul endroit la situation possible d'un bogue +donné. Vous pouvez circonscrire le bogue. +

+ +

Gain de souplesse

+ +

+En utilisant des fonctions, vous donnez à vos programmes une grande +souplesse. Si vous voulez apporter une modification d'ensemble à un +programme, vous n'avez plus à corriger autant de morceaux du script +qu'il y a d'occurrences du même fragment : vous apportez vos +modifications de manière centralisée. +

+ +

+Pour toutes ces raisons, n'hésitez surtout pas à créer des fonctions et +à les emboîter entre elles. La programmation vous paraîtra de plus en +plus facile, à mesure que vous réaliserez des tâches de plus en plus +complexes. +

+ + +
+Auteur : Baptiste Mélès. +Dernière modification le . +
+ + +