| edix_shell_script(7f) | edix_shell_script(7f) |
NOM
edix_shell_script - L'écriture des scripts shell
DESCRIPTION
On découvre usuellement les commandes du shell avec la pratique du terminal sous unix (voir edix_shell(7f), edix_terminal(7f) et edix_unix(7f)). Mais les commandes du shell peuvent aussi être saisies dans un fichier (un "script shell") de façon a créer un programme. Dans le cadre d'edix, les scripts shell portent l'extension .sh, par exemple ${EDIX}/bin/mise_a_jour_edix.sh est un script utilisé pour récuppérer la dernière version du projet edix.
Nous indiquons ici quelques éléments de la pratique d'écriture des scripts shell.
CREATION, VALIDATION ET EXECUTION D'UN SCRIPT
Nous allons prendre un exemple très simple : un script qui aurait comme objectif d'afficher le nom du répertoire courant, puis d'afficher le contenu de ce répertoire. Si nous souhaitions réaliser ces deux actions dans un terminal il suffirait de saisir
etudiant:~ $ pwd etudiant:~ $ ls
Nous allons créer un script nommé premier_script.sh qui réalise ces deux actions.
Création du script
Nous allons créer le fichier premier_script.sh qui contiendra trois lignes. La première ligne définira le statut de ce fichier, c'est à dire indiquera qu'il s'agit d'un script shell. La seconde et la troisième ligne contiendront les deux commandes :
etudiant:~ $ vim premier_script.sh vim:
#!/bin/sh
pwd
ls
Ensuite on rend le script exécutable avec la commande suivante :
etudiant:~ $ chmod +x premier_script.sh
Validation du script
Avant d'exécuter un script, vous êtes fortement invités à contrôler sérieusement son contenu. Par exemple, si votre script efface un fichier, alors le fichier sera effacé et il n'y aura pas de retour en arrière. Une façon de faire peut être d'excécuter d'abord chaque ligne, une par une (en réfléchissant), dans un terminal.
La validation doit aussi être considérée sur un autre plan : s'assurer que le script est bien compatible avec la norme posix (voir edix_shell_posix(7f)). Pour cela on lance la commande suivante (voir unix_shellcheck(7f)) :
etudiant:~ $ shellcheck -x -o all premier_script.sh
Exécution du script
Une fois que le script est validé, vous pouvez l'exécuter avec
etudiant:~ $ ./premier_script.sh
LES ENTREES D'UN SCRIPT
Si on souhaite que le script prenne en entrée un certain nombre de chaines de caractères, par exemple si on souhaite créer un script nommé second_script.sh qui prend en entrée deux chaines de caractères, la chaine repertoire et la chaine fichier et qui execute la commande ls -l sur repertoire/fichier, alors on utilise les variables positionnelles 1 et 2 :
etudiant:~ $ vim second_script.sh vim:
#!/bin/sh
ls -l "${1}/${2}"
que l'on exécute avec
etudiant:~ $ ./second_script.sh repertoire fichier
Dans le script ${1} reçoit repertoire alors que ${2} reçoit fichier.
LA GESTION DES ERREURS
Interrompre le script à la première erreur
Par défaut, à l'exécution d'un script, si une erreur se produit sur une ligne du script, alors le shell poursuit l'exécution des lignes suivantes. On peut décider au contraire que le script s'interrompt dès la première erreur. Pour cela on place en début de script la ligne suivante :
set -e
Par exemple, le script suivant appelle ls -l sur le fichier ${1}/${2}, puis il affiche le contenu de ce fichier avec la commande cat. Le fait que l'on utilise le set -e fait que que si le ls -l échoue (par exemple lorsque le fichier indiqué n'existe pas), alors le script s'interrompt et le cat n'est pas exécuté :
#!/bin/sh
set -e
ls -l "${1}/${2}"
cat "${1}/${2}"
Détecter un problème et retourner un message d'erreur
On peut aussi choisir de détecter activement des erreurs spécifiques et interrompre le script en envoyant un message sur la sortie d'erreur. Pour cela, après détection de l'erreur, on utilise les commandes suivantes :
printf "%sn" "message d'erreur" >&2
exit 1
Par exemple, on peut détecter que ${1}/${2} n'existe pas avec le test suivant,
test ! -f "${1}/${2}"
et renvoyer un message d'erreur :
#!/bin/sh
set -e
if test ! -f "${1}/${2}"
then
printf "%sn" "${1}/${2} n'existe pas" >&2
exit 1
fi
ls -l "${1}/${2}"
cat "${1}/${2}"
Simplifier l'écriture des messages d'erreur
Si on doit faire plusieurs tests d'erreur, le script peut devenir difficile à lire. On peut alors utiliser une fonction erreur :
#!/bin/sh
set -e
erreur()
{
printf "%sn" "${@}" >&2
exit 1
}
test -f "${1}/${2}" || erreur "${1}/${2} n'existe pas"
ls -l "${1}/${2}"
cat "${1}/${2}"
Vérifier le nombre de variables d'entrées
La variable # contient le nombre de variables qui ont été passées en entrée. Dans notre exemple, on peut souhaiter vérifier que l'utilisateur a bien passé deux variables en entrée et renvoyer un message d'erreur dans le cas contraire :
#!/bin/sh
set -e
erreur()
{
printf "%sn" "${@}" >&2
exit 1
}
test ${#} -eq 2 || erreur "Utilisation : ${0} <répertoire> <fichier>"
test -f "${1}/${2}" || erreur "${1}/${2} n'existe pas"
ls -l "${1}/${2}"
cat "${1}/${2}"
LES OPERATIONS NUMERIQUES
On cherche souvent à utiliser des scripts shell qui contiennent des opérations numériques (des calculs). Cette question est délicate : le shell est fondamentalement construit à partir du concept de chaîne de caractère et n'a donc rien dédié au calcul numérique.
Rester en shell ou passer en C ?
Il y a un choix à faire :
- essayer de réaliser les calculs numériques en shell malgré le fait que le shell soit plutôt mal adapté au calcul nuérique ;
- ou créer un ou plusieurs exécutables en langage C et les appeler depuis le shell.
Utiliser bc
Assez fondamentalement, les variables du shell sont des chaines de caractères. Pour les opérations de calcul, on utilise la calculatrice bc(1p).
Imaginons que nous disposons d'une variable $var1 dont le contenu s'interprète numériquement et que nous souhaitons ajouter 1 à cette variable. La commande suivante réalise cette incrémentation :
etudiant:~ $ var1="$( printf "%s\n" "$var1 + 1" | bc -l )"
Toutes les opérations de calcul peuvent être conçues de la même façon, en exploitant les potentialités de bc.
RQ: Attention aux opérations avec les variable en second terme. Par exemple "1 - $var1" peut ne pas fonctionner si var1=-3 car cela revient à écrire "1 --3" que bc ne comprend pas. Il faut donc écrire "1 - ($var1)".
Avant d'utiliser bc dans un script, vous pouvez tester si les opérations que vous utilisez sont bien valides en lançant bc en mode interactif :
etudiant:~ $ bc -l
TESTS
Les chaines de caractères vraies ou fausses
Une chaine de caractère peut être vraie ou fausse. Par exemple
1 = 1
est vraie, alors que
1 = 2
est fausse.
Suite sur les types de chaines utilisées dans le contexte des tests : à écrire
La commande test
La commande test contrôle qu'une chaine est vraie. Par exemple
etudiant:~ $ test 1 = 1 etudiant:~ $ printf "$?"
produit
0
car le test est un succès, alors que
etudiant:~ $ test 1 = 2 etudiant:~ $ printf "$?"
produit
1
car le test est un échec
if ... then ... else
à écrire
case
Le case suivant regarde si il y a un e dans var, sinon il regarde si il a un a :
case "${var}" in
*e*)
printf "%sn" "Il y a un e dans var"
;;
*a*)
printf "%sn" "Il n'y a pas de e dans var mais il y a un a"
;;
*)
printf "%sn" "Il n'y a ni e ni a dans var"
;;
esac
BOUCLES
Boucles for
Une boucle for prend en entree une suite de chaines de caractères séparées par un espace. Pour chacune des chaines, le contenur de la chaine est placée dans la variable d'indexation (ici var qui reçoit donc successivement chaine1, chaine2, chaine3 et chaine4) et les lignes de shell placées entre do et done sont exécutée (ici il n'y a qu'une ligne qui imprime le contenu de var) :
suite="chaine1 chaine2 chaine3 chaine4"
for var in ${suite}
do
printf "%s\n" "${var}"
done
Cette boucle produit
chaine1
chaine2
chaine3
chaine4
Si on veut une boucle numérique, on peut utiliser la fonction seq :
suite=$(seq 1 6)
for var in ${suite}
do
printf "%s\n" "${var}"
done
produit
1
2
3
4
5
6
Boucles while
i=1 while [ "$i" -ne 6 ] do
printf "%s\n" "$i"
i=$((i + 1)) done
produit
1
2
3
4
5
VOIR AUSSI
bc(1p), edix_shell(7f), edix_shell_posix(7f), edix_terminal(7f), edix_unix(7f)
| 2026-06-10 | UNIX |