Chapitre 11. Fonctions

Table des matières

1. Introduction
1.1. Qu'est-ce qu'une fonction?
1.2. La syntaxe des fonctions
1.3. Les paramètres positionnels dans les fonctions
1.4. Afficher une fonction
2. Exemples de fonctions dans des scripts
2.1. Recyclage
2.2. Définir le chemin
2.3. Sauvegarde à distance
3. Résumé
4. Exercices

Résumé

Dans ce chapitre nous aborderons:

  • Qu'est-ce qu'une fonction

  • Création et affichage de fonctions depuis la ligne de commande

  • Fonctions dans les scripts

  • Passer des arguments à une fonction

  • Quand utiliser une fonction

1. Introduction

1.1. Qu'est-ce qu'une fonction?

La fonction Shell est le moyen de grouper des commandes en vue d'une exécution ultérieure, par l'appel d'un nom donné à ce groupe, ou routine. Le nom de la routine doit être unique dans le Shell ou le script. Toutes les commandes qui constituent une fonction sont exécutées comme des commandes régulières. Quand une fonction est appelée comme le nom d'une simple commande, la liste des commandes associées à ce nom de fonction est traitée. Une fonction est exécutée à l'intérieur du Shell dans lequel elle a été déclarée: aucun processus nouveau n'est créé pour interpréter les commandes.

Les commandes intégrées spéciales sont détectées avant les fonctions Shell pendant l'analyse des commandes. Les intégrées spéciales sont: break, :, ., continue, eval, exec, exit, export, readonly, return, set, shift, trap et unset.

1.2. La syntaxe des fonctions

Les fonctions emploient plutôt la syntaxe

function FONCTION { COMMANDES; }

ou

FONCTION () { COMMANDES; }

Chacune définit une fonction Shell FONCTION. L'emploi de la commande intégrée function est optionnel; cependant, si elle n'est pas employée, les parenthèses sont nécessaires.

Les commandes listées entre les accolades forment le corps de la fonction. Ces commandes sont exécutées du moment que FONCTION est spécifié en tant que nom de commande. Le statut d'exécution est celui de la dernière commande exécutée dans le corps de la fonction.

Erreurs communes

Les accolades doivent être séparées du corps de la fonction par un espace, sinon elles sont interprétées d'une mauvaise façon.

Le corps d'une fonction doit se terminer par un point-virgule ou un saut de ligne.

1.3. Les paramètres positionnels dans les fonctions

Les fonctions sont comme des mini-scripts: elles peuvent accepter des paramètres, elles peuvent utiliser des variables connues seulement dans la fonction (avec l'intégrée Shell local) et elles peuvent renvoyer des valeurs au Shell appelant.

Une fonction a aussi un système pour interpréter des paramètres positionnels. Cependant, les paramètres positionnels passés à une fonction n'ont pas nécessairement les mêmes valeurs que ceux passés à une commande ou un script.

Quand une fonction est exécutée, les arguments de la fonction deviennent les paramètres positionnels durant son exécution. Le paramètre spécial # qui est interprété comme le nombre de paramètres positionnels est mis à jour pour refléter ce changement. Le paramètre positionnel 0 est inchangé. La variable Bash FUNCNAME est définie avec le nom de la fonction, lorsqu'elle s'exécute.

Si l'intégrée return est exécutée dans une fonction, la fonction s'interrompt et l'exécution reprend avec la commande qui suit la fonction appelée. Quand une fonction s'achève, les valeurs des paramètres positionnels et le paramètre spécial # sont restaurés à la valeur qu'ils avaient avant l'exécution de la fonction. Si un argument numérique est donné à return, c'est ce statut qui est retourné. Un exemple simple:

[lydia@cointreau ~/test] cat showparams.sh
#!/bin/bash
                                                                                
echo "Ce script montre l'emploi d'arguments de fonction."
echo
                                                                                
echo "Le paramètre positionnel 1 pour le script est $1."
echo
                                                                                
test ()
{
echo "Le paramètre positionnel 1 pour la fonction est $1."
RETURN_VALUE=$?
echo "Le code retour de cette fonction est $RETURN_VALUE."
}
                                                                                
test other_param

[lydia@cointreau ~/test] ./showparams.sh parameter1
Ce script montre l'emploi d'arguments de fonction.
 
Le paramètre positionnel 1 pour le script est 1.
 
Le paramètre positionnel 1 pour la fonction est other_param.
Le code retour de cette fonction est 0.

[lydia@cointreau ~/test]

Notez que la valeur retournée ou code retour de la fonction est souvent stockée dans une variable, afin qu'elle puisse être testée ultérieurement. Les scripts d'initialisation de votre système souvent emploient la technique de tester la variable RETVAL, comme ceci:

if [ $RETVAL -eq 0 ]; then
	<lancer le démon>

Ou comme l'exemple dans ce script /etc/init.d/amd, où les possibilités d'optimisation de Bash sont utilisées:

[ $RETVAL = 0 ] && touch /var/lock/subsys/amd

Les commandes après && ne sont exécutées que si le test rend vrai; c'est une façon plus rapide de représenter une structure if/then/fi.

Le code retour de la fonction est souvent utilisé comme statut d'exécution de tout le script. Vous verrez beaucoup de scripts d'initialisation finissant avec quelque chose comme ça exit $RETVAL.

1.4. Afficher une fonction

Toutes les fonctions connues du Shell courant peuvent être affichées avec l'intégrée set sans options. Une fonction est conservée après avoir été appelée, à moins qu'elle soit unset après son exécution. La commande which affiche aussi les fonctions:

[lydia@cointreau ~] which zless
zless is a function
zless ()
{
    zcat "$@" | "$PAGER"
}

[lydia@cointreau ~] echo $PAGER
less

Ceci est le type de fonctions qui sont typiquement configurées dans un fichier de configuration des ressources Shell de l'utilisateur. Les fonctions sont plus flexibles que les alias et fournissent un moyen simple et facile d'adapter l'environnement utilisateur.

En voici un pour les utilisateurs DOS:

dir ()
{
    ls -F --color=auto -lF --color=always "$@" | less -r
}

2. Exemples de fonctions dans des scripts

2.1. Recyclage

Il y a plein de scripts sur votre système qui utilisent des fonctions comme un moyen structuré de passer une série de commandes. Sur certains systèmes Linux, par exemple, vous trouverez le fichier de définition /etc/rc.d/init.d/functions, qui est invoqué dans tous les scripts d'initialisation. Avec cette méthode, les tâches communes comme contrôler qu'un processus s'exécute, démarrer ou arrêter un démon etc., n'ont qu'à être écrites qu'une seule fois, d'une manière générique. Si la même tâche est nécessaire de nouveau, le code est recyclé. Dans ce fichier de fonctions la fonction checkpid:

# Check if $pid (could be plural) are running
checkpid() {
        local i

        for i in $* ; do
                [ -d "/proc/$i" ] && return 0
        done
        return 1
}

Cette fonction est appelée dans ce même script par d'autres fonctions, lesquelles sont appelées dans d'autres scripts. La fonction daemon, par exemple, est utilisée dans la plupart des scripts de démarrage pour démarrer un processus serveur (sur les machines qui utilisent ce système).

2.2. Définir le chemin

Le code ci-dessous peut être trouvé dans le fichier /etc/profile. La fonction pathmunge est définie, puis utilisée pour définir les chemins de root et des autres utilisateurs:

pathmunge () {
        if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
           if [ "$2" = "after" ] ; then
              PATH=$PATH:$1
           else
              PATH=$1:$PATH
           fi
        fi
}

# Path manipulation
if [ `id -u` = 0 ]; then
        pathmunge /sbin
        pathmunge /usr/sbin
        pathmunge /usr/local/sbin
fi

pathmunge /usr/X11R6/bin after

unset pathmunge

La fonction considère son premier argument comme étant des noms de chemin. Si ce nom de chemin n'est pas encore dans le chemin courant, il y est ajouté. Le second argument de cette fonction définit si le chemin sera ajouté au début ou à la fin de la définition actuelle du PATH.

L'utilisateur standard se voit ajouter seulement /usr/X11R6/bin dans leurs chemins, alors que root se voit adjoindre quelques répertoires supplémentaires contenant les commandes systèmes. Après avoir été utilisée, la fonction est supprimée par unset.

2.3. Sauvegarde à distance

L'exemple suivant est l'un de ceux que j'utilise pour faire mes sauvegardes des fichiers de mes livres. Il emploie des clés SSH pour effectuer la connection à distance. Deux fonctions sont définies, buplinux et bupbash, qui font chacune un fichier .tar, qui est alors compressé et envoyé vers le serveur distant. Ensuite, la copie locale est supprimée.

Le dimanche, seul bupbash est exécuté.

#/bin/bash

LOGFILE="/nethome/tille/log/backupscript.log"
echo "Starting backups for `date`" >> "$LOGFILE"

buplinux()
{
DIR="/nethome/tille/xml/db/linux-basics/"
TAR="Linux.tar"
BZIP="$TAR.bz2"
SERVER="rincewind"
RDIR="/var/www/intra/tille/html/training/"

cd "$DIR"
tar cf "$TAR" src/*.xml src/images/*.png src/images/*.eps
echo "Compressing $TAR..." >> "$LOGFILE"
bzip2 "$TAR"
echo "...done." >> "$LOGFILE"
echo "Copying to $SERVER..." >> "$LOGFILE"
scp "$BZIP" "$SERVER:$RDIR" > /dev/null 2>&1
echo "...done." >> "$LOGFILE"
echo -e "Done backing up Linux course:\nSource files, PNG and EPS images.\nRubbish removed." >> "$LOGFILE"
rm "$BZIP"
}

bupbash()
{
DIR="/nethome/tille/xml/db/"
TAR="Bash.tar"
BZIP="$TAR.bz2"
FILES="bash-programming/"
SERVER="rincewind"
RDIR="/var/www/intra/tille/html/training/"

cd "$DIR"
tar cf "$TAR" "$FILES"
echo "Compressing $TAR..." >> "$LOGFILE"
bzip2 "$TAR"
echo "...done." >> "$LOGFILE"
echo "Copying to $SERVER..." >> "$LOGFILE"
scp "$BZIP" "$SERVER:$RDIR" > /dev/null 2>&1
echo "...done." >> "$LOGFILE"

echo -e "Done backing up Bash course:\n$FILES\nRubbish removed." >> "$LOGFILE"
rm "$BZIP"
}

DAY=`date +%w`

if [ "$DAY" -lt "2" ]; then
  echo "It is `date +%A`, only backing up Bash course." >> "$LOGFILE"
  bupbash
else
  buplinux
  bupbash
fi


echo -e "Remote backup `date` SUCCESS\n----------" >> "$LOGFILE"

Ce script est lancé par cron, c'est à dire sans intervention de l'utilisateur, c'est pour ça que le standard d'erreurs de la commande scp est redirigé sur /dev/null.

Il pourrait être observé que toutes les étapes peuvent être combinées en une commande tel que

tar c dir_to_backup/ | bzip2 | ssh server "cat > backup.tar.bz2"

Cependant, si vous êtes intéressé par les résultats intermédiaires, qui pourrait être récupérés en cas d'échec du script, ce n'est pas ce qu'il faut écrire.

L'expression

command &> file

est équivalent à

command > file 2>&1

3. Résumé

Les fonctions fournissent un moyen facile de grouper des commandes que vous avez besoin d'exécuter régulièrement. Quand une fonction tourne, les paramètres positionnels sont ceux de la fonction. Quand elles s'arrêtent, on retrouve ceux du programme appelant. Les fonctions sont comme des mini-scripts, et comme un script, elles génèrent un code retour.

Bien que ce chapitre soit court, il contient des connaissances importantes nécessaires pour atteindre le stade suprême de la paresse, ce qui est le but recherché de tout administrateur système.

4. Exercices

Voici quelques tâches utiles que vous pouvez réalisez avec des fonctions.

  1. Ajoutez une fonction à votre fichier de configuration ~/.bashrc qui automatise l'impression des pages man. L'effet devrait être que quand vous taper printman <commande> les pages man de la commande sortent de l'imprimante. Contrôler le fonctionnement avec une pseudo imprimante.

    En plus, imaginez la possibilité pour l'usager de demander un numéro de section des pages man.

  2. Créer un sous-répertoire dans votre répertoire racine dans lequel vous pouvez stocker des définitions de fonctions. Y mettre quelques fonctions. Des fonctions utiles peuvent être, parmi d'autres, celles qui permettent les mêmes commandes que DOS ou un UNIX commercial, ou vice versa. Ces fonctions devraient être importées dans votre environnment Shell quand ~/.bashrc est lu.