Afin d'éviter autant que possible la duplication de code, le
fichier src/home/netlogon/clients-linux/lib.sh
est une
petite boîte à outils de fonctions shell réutilisables dans
d'autres scripts shell, sous deux conditions.
En effet, lors du build du package :
- si un script shell se trouve dans
src/
, - et s'il contient la ligne
###LIBSH###
alors cette ligne sera ni plus ni moins remplacée par le
contenu du fichier lib.sh
.
Et ainsi, toutes fonctions de cette boîte à outils seront disponibles pour le script shell.
Remarque : une autre façon d'utiliser dans un script les fonctions contenues dans le fichier lib.sh
est de le télécharger et de l'exécuter dans ce script en y incorporant la fonction recuperer_lib_sh
suivante et en l'appelant en début du script (c'est le cas des scripts de post-installation du paquet pxe-clients-linux
) :
recuperer_lib_sh()
{
# Récupération de la librairie lib.sh des fonctions shell
wget -q http://${ip_se3}/install/lib.sh
if [ "$?" = "0" ]
then
echo "la librairie de fonctions lib.sh a bien été récupérée" | tee -a $compte_rendu
chmod +x lib.sh
mv lib.sh /root/bin/lib.sh
. /root/bin/lib.sh
else
echo "${rouge}échec de la recupération de la libraire lib.sh" | tee -a $compte_rendu
sleep 5
fi
}
Le contenu de lib.sh
ne devra rien exécuter du tout et
faire uniquement des déclarations de fonctions (une
déclaration de fonction, c'est toujours inoffensif).
Peut-être que le fichier lib.sh
sera amené à être inclus
ailleurs que dans du shell bash, par exemple dans du shell
/bin/sh
.
Par conséquent, ne pas utiliser cette déclaration de fonction :
function my_function () {
# ...
}
Utiliser cette forme de déclaration (qui est POSIX) :
my_function () {
# ...
}
ou bien cette forme, as you like :
ma_fonction()
{
# ...
}
D'une manière générale, éviter le plus possible les syntaxes
propres à bash. Un bon moyen de tester son code sur ce point
là consiste à juste changer le shebang du script et mettre
#!/bin/sh
au lieu de #!/bin/bash
.
C'est sans doute le point le plus important. Les fonctions ne doivent pas changer l'état du script appelant (le script « appelant » d'une fonction est simplement le script qui lance l'exécution de la fonction).
Par exemple ceci est à éviter impérativement :
my_function () {
var='xxxxx'
#...
}
En effet, la variable var
ici n'est pas locale à la
fonction. Par exemple, imaginez qu'il y ait plus loin dans
le code ceci :
var='valeur-super-importante'
# Appel de la fonction.
my_function
echo "$var"
Le echo
affichera xxxxx
et non pas
valeur-super-importante
comme on pourrait le penser de
prime abord, car l'exécution de la fonction a changé la
valeur de la variable var
du script appelant. Pour éviter
cela, déclarez dans les fonctions uniquement des
variables locales avec le mot clé local
:
my_function () {
local var='xxxxx'
# Rq: la syntaxe suivante est également possible
# et totalement équivalente.
#
# local var
# var='xxxxx'
#...
}
Cette fois-ci, si une variable var
est déjà définie au
niveau du script appelant, celle-ci ne sera pas modifiée par
l'exécution de la fonction my_function
(il s'agit de deux
variables qui certes portent le même nom même mais qui se
trouvent dans des zones de la mémoire RAM différentes).
Si jamais vous avez besoin qu'une fonction utilise une
variable du script appelant, passez-la en argument de la
fonction. Par exemple, dans lib.sh
:
my_function () {
# $1 est le premier argument qui est passé à la fonction.
# Il est vide s'il n'y a pas d'argument. $2 est le deuxième
# argument passé à la fonction, et ainsi de suite.
local var="$1"
# Remarquez au passage que la variable var est bien locale
# à la fonction. Elle contiendra simplement une copie de
# l'argument qui est passé à la fonction.
# Code où l'on fait usage de la variable local var...
}
Et dans le script appelant, on pourra alors utiliser la fonction ainsi :
var='valeur-super-importante'
# Appel de la fonction. L'argument var est ici une variable
# du script appelant. La fonction va juste copier son
# contenu dans sa variable locale var.
my_function "$var"
Si vous souhaitez changer la valeur d'une variable du script appelant, vous pouvez faire comme ceci :
# Dans lib.sh.
update_var () {
local var="$1"
# Du code où var est modifiée etc...
# Ceci est globalement équivalent à un « echo "$var"
# mais en plus robuste.
printf '%s\n' "$var"
}
# Dans le script appelant.
var='a_default_value'
# On met à jour la valeur de "var". La modification de la
# valeur de "var" est explicitement faite au niveau du
# script appelant, elle n'est pas enfouie dans le corps de
# la fonction "update_var".
var=$(update_var "$var")
Faire en sorte que le code de retour de la fonction reflète le bon déroulement ou non de son exécution.
Pour cela utilisez l'instruction return
.
Une fonction shell doit :
- retourner 0 si tout s'est bien passé (c'est d'ailleurs ce que font toutes les commandes du shell comme grep par exemple),
- retourner un entier entre 1 et 255 (inclus) si tout ne s'est pas bien passé.
Voici un exemple mais ce n'est pas la seule façon de faire :
my_function () {
cmd1
cmd2
if truc1_pas_ok
then
return 1
fi
if truc2_pas_ok
then
return 2
fi
# ...
# Bon, tout est Ok alors on peut renvoyer 0.
return 0
}
Remarque : si on ne précise pas d'instruction return
,
le code de retour de la fonction sera celui de sa dernière
commande exécutée (ce qui peut être pratique parfois).
Par exemple avec :
my_function() {
# Le code de retour de la fonction sera celui de la
# commande cmd3.
cmd1
cmd2
cmd3
}
Pour le code de retour d'une fonction, voir le point ci-dessus. La sortie d'une fonction n'a rien à voir à priori. La sortie est simplement ce qu'une fonction affiche.
C'est important car cela permet de récupérer de nouvelles valeurs et de les stocker dans des variables du script appelant.
Par exemple :
generate_password() {
local passwd
# Du code...
echo "$passwd"
# Inutile ici, le code de retour sera celui de la
# commande echo, c'est-à-dire 0 a priori.
#
#return 0
}
Et dans le script appelant :
# La variable "passwd" contiendra la valeur de la variable
# locale "passwd" de la fonction generate_password.
passwd=$(generate_password)
Ici, on a donc la fonction generate_password
:
- dont le code de retour est 0,
- dont la sortie affiche un mot de passe (qui est récupéré dans la variable "passwd" du script appelant).
Pour récupérer la sortie d'une fonction (et de n'importe quelle commande en général), on fait :
var=$(my_function)
Et pour récupérer le code de retour d'une fonction (et de
n'importe quelle commande en général), on peut utiliser la
variable spéciale $?
qui contient toujours le code de
retour de la dernière commande exécutée.