Outils pour utilisateurs

Outils du site


informatique:linux:ecriture_de_script_ecriture_de_scripts_bash

Comment :

Le shell est un interpréteur de commande qui sert d'interface entre l'utilisateur et le système d'exploitation. Bash est le plus utilisé des shells, c'est celui sur lequel nous nous baserons tout au long de ce chapitre. De plus, Bash est aussi un language de programmation complet visant à simplifier et automatiser les taches quotidiennes des administrateurs.

Creation et invocation d'un script

Creation d'un script

Un script, ou fichier de commandes, est un ensemble de commandes placées dans un fichier texte. Chaque ligne de ce fichier correspond à une ligne qu'aurait pu entrer un opérateur. Un script est destiné à un interpréteur qui lit, analyse, et exécute les commandes qu'il contient. Dans le cas d'un shell-script, l'interpréteur est un des shells d'Unix (bash, sh…). Dans ce document, nous allons apprendre à écrire un script en Bourne-shell (bash ou sh). La première ligne d'un script doit spécifier le programme à appeler pour l'interpréter. Elle doit commencer par la séquence “#!” suivie d'au moins une espace puis du nom de l'interpréteur. Pour un shell-script écrit en Bourne-shell, elle sera :

#! /bin/bash 

Pour un script Perl, la première ligne sera :

#! /usr/bin/perl 

Le nom d'un shell-script peut être quelconque, mais on choisit généralement :

  • soit “mon script” (sans extension), pour que son nom ait l'air d'une commande Unix comme une autre.
  • soit “mon script.sh” pour indiquer explicitement qu'il s'agit d'un shell-script.

Invocation d'un script

On peut exécuter un script comme toute autre commande en l'appelant par son nom. Pour cela, il faut d'une part que les droits d'exécution soient positionnés correctements :

chmod +x mon_script 

et, d'autre part, il faut que le chemin du fichier appartienne à la variable d'environnement PATH (sinon, vous devriez systématiquement préciser le chemin de votre script pour l'exécuter. Une autre façon d'exécuter un script, et qui aboutit au même résultat, est d'invoquer son interpréteur avec le nom du script en paramètre (exemple : “sh mon script.sh”). Il faut savoir que, lancé ainsi, un script s'exécute dans un shell secondaire (sous-shell), totalement indépendant du shell qui l'a appelé Ceci aura pour effet d'annuler toute modification de l'environnement du script (changement de chemin avec cd, création ou modification de variables, etc…) dès lors que le script sera terminé. Pour éviter cela, et donc permettre à un shell-script de modifier l'environnement de votre shell, il suffit d'appeler ce script grâce à la commande source (ou '.').

. mon_script 

ou

source mon_script 

Toutes les commandes du script sont alors exécutées dans le shell actuel. Premier exemple de script

#! /bin/bash 
echo hello world 
Execution de notre script : 
$ ./hello.sh 
Hello world 
Une autre facon de le lancer, cette fois-ci en appelant le script par l'interpreteur : 
$ bash hello.sh 
Hello World 
Enfin de cette facon le script est lancé dans le shell en cours : 
$ . hello.sh ou source hello.sh 
Hello World

Commentaires et taches de fond

Chaque ligne commençant par un # ne sera pas traitée comme une commande mais comme un commentaire par le shell.

#! /bin/bash 
#debut du script 
echo hello world 
#fin du script

De plus, toute commande suivie du caractère '&' s'executera en tâche de fond (arrière plan).

Valeur retournée par un shell-script a la fin de son execution

C'est la valeur retournée par la dernière commande exécutée dans le script. Par convention, une valeur de retour égale à 0 signifie que le script s'est bien déroulé. Inversement, toute autre valeur indique une erreur. De plus, on peut fixer cette valeur par la commande : exit n Dans ce cas, c'est n qui est retourné. Comme pour toute commande, la valeur de retour d'un script peut être récupérée dans la variable $?.

Commandes d'un shell script

Commandes simples

Dans un script, on peut employer toute commande exécutable par l'utilisateur :

  • un mot-clé appartenant au Bourne-shell (exemple : if, case, …)
  • une commande interne (builtin) au Bourne-shell (exemple : echo, umask,…)
  • une fonction définie par l'utilisateur avec le mot-clé function
  • une commande externe (commande appartenant aux chemins de la variable PATH).

Pour savoir d'où provient une commande, on peut utiliser type.

$ type while 
while is a shell keyword 
$ type cd 
cd is a shell builtin 
$ type lsc 
lsc is aliased to 'ls -color=auto' 
$ type mkdir 
mkdir is /bin/mkdir

Commandes composées

Les exemples qui suivent sont donnés avec deux commandes mais peuvent être étendus à N commandes.

Commandes sequentielles

commande1 ; commande2 Exécute commande1 puis commande2. Le point-virgule permet de réunir sur une seule ligne deux instructions qui devraient être sur deux lignes séparées. Lorsque c'est possible, son utilisation est à éviter puisqu'elle nuit à la lisibilité.

Le pipe

commande1 | commande2 Exécute commande1 et envoi le résultat à commande2. Grâce au tube, commande2 effectue son traitement sur le texte qu'aurait affiché commande1. Attention, commande1 et commande2 s'exécutent dans deux shells secondaires (sous-shells) et, par conséquent, toute modification qu'ils apportent à l'environnement (variables, chemin actuel,…) est perdue dès la fin de la commande.

Commandes en parallèles

commande1 & commande2 Exécute commande1 et commande2 en parallèles. commande1 est exécutée en tâche de fond.

Commande sur erreur

commande1 || commande2 Exécute commande2 si et seulement si commande1 a échouée ; généralement utilisé dans les conditions de structures de type if, while et until. .

Commandes sur réussite

commande1 && commande2 Exécute commande2 si et seulement si commande1 s'est bien déroulée. Généralement utilisé dans les conditions des structures if, while et until.

Commandes en paramètre

commande1 $(commande2) ou commande1 `commande2` Cette combinaison exécute commande1 avec en paramètre le texte qu'aurait affiché commande2. Exemple :

#! /bin/sh 
# 
# Shell-script de mise en application de la récupération du résultat 
# d une commande. Affiche l'heure en français. 
heures=$(date +%H) 
minutes=`date +%M` 
echo "Il est $heures heures $minutes minutes et $(date +%S) secondes" 
$ ./heure

Il est 12 heures 29 minutes et 42 secondes Les deux variantes proposées sont strictement identiques. On peux utiliser l'une ou l'autre pour des raisons de commodités.

Utilisation des parenthèses et des accolades

{ commande1 ; commande2; }, ( commande1; commande2 ) Les parenthèses et accolades permettent d'encadrer une série de commandes séparées les unes des autres par “&”, “&&”, “|”, “||” ou “ ;”. L'usage des parenthèses ou des accolades permet à l'ensemble des commandes qu'elles contiennent de subir une redirection. En effet, avec la commande “ls src ; ls bin | lpr”, seul le contenu du répertoire bin est imprimé car une redirection ne s'applique qu'à la commande qui précède. Par contre, la commande “ {ls src ; ls bin ; } | lpr” provoque bien l'impression du contenu des répertoires src et bin. On préférera généralement les accolades aux parenthèses car les instructions situées entre parenthèses s'exécutent dans un shell secondaire indépendant du shell actuel (voir l'utilisation du tube, plus haut). Attention, la dernière commande de la liste entre accolades doit être terminée par un point-virgule.

Ecriture

La commande “echo texte” permet d'écrire un texte à l'écran. Bien que ce ne soit pas nécessaire, pour éviter tout problème il est vivement conseillé d'encadrer le texte à afficher par des guillemets. Si le texte à afficher est une chaîne vide, la commande provoque un saut de ligne. L'option -n évite le saut de ligne automatique à la fin du texte. L'option -e permet l'interprétation des caractères spéciaux utilisés avec printf(), tels l'alarme, le retour à la ligne, la tabulation, etc… Exemples :

echo "Texte à 
afficher" 
echo "" 
echo -n "Ce texte ne sera pas suivi d'un saut de ligne" 
echo -e "\t\aCe texte commence par une tabulation et émet un signal sonore."

Lecture au clavier

La commande read utilisée seule permet de lire une phrase complète. La phrase lue est stockée dans la variable REPLY. La commande “read mot1 mot2 reste” permet aussi de lire une phrase au clavier mais son premier mot est affecté à la variable mot1, son deuxième mot est affecté à mot2 et le reste de la phrase est affecté à la variable reste. Exemple : luser@localhost$ echo -n “Entrez votre nom de login et votre nom civil: ”; read log nom Entrez votre nom de login et votre nom civil: moliere Jean-Baptiste Poquelin luser@localhost$ echo “$log est le nom de login de $nom” moliere est le nom de login de Jean-Baptiste Poquelin Attention : une opération du style “cat fichier | read ligne” ne fonctionnera pas car l'instruction read (tout comme l'instruction cat) sera exécutée dans un shell indépendant et le contenu de la variable ligne sera perdu dès la fin de l'opération. Par contre “read ligne < fichier” fonctionnera.

La commande select

La commande select permet d'offrir un menu à l'utilisateur. Sa syntaxe est : select variable in “choix1” “choix2” … “choixN” do liste d'instructions done Le menu présenté comporte l'ensemble des choix proposés précédés d'un numéro. Une invite est affichée et le shell attend une entrée au clavier. La variable PS3 doit contenir la chaîne d'invite (“# ? ” par défaut). Lorsque l'utilisateur a entré son choix, le corps de la boucle est exécuté. La variable REPLY contient alors ce que l'utilisateur a rentré (un numéro normalement). Si le choix de l'utilisateur correspond à l'une des propositions qui lui sont faites, la variable “variable” contient aussi la chaîne correspondante (“choix1”, “choix2”, … ou “choixN”). Exemple :

#! /bin/sh 
# 
# menu 
# 
# Shell-script de mise en application de l'instruction select. 
PS3="Entrez le numéro de votre commande -> " 
echo "Que désirez-vous boire ?" 
select boisson in "Rien, merci" "Café" "Thé 
au lait" "Chocolat"; do 
if [ -z "$boisson" ]; then 
echo "Erreur: entrez un des chiffres proposés." 1>&2 
elif [ "$REPLY" -eq 1 ]; then 
echo "Au revoir!" 
break 
else 
echo "Vous avez fait le choix numéro $REPLY..." 
echo "Votre $boisson est servi." 
fi 
echo 
done 
Test:

luser@localhost$ ./menu Que désirez-vous boire ? 1) Rien, merci 2) Café 3) Thé au lait 4) Chocolat Entrez le numéro de votre commande → 3 Vous avez fait le choix numéro 3… Votre Thé au lait est servi. 1) Rien, merci 2) Café 3) Thé au lait 4) Chocolat Entrez le numéro de votre commande → 1 Au revoir! luser@localhost$

Redirections des entrées-sorties standard

Avant tout, il faut savoir que toute redirection ne s'applique qu'à la dernière commande. Dans le cas d'un série de commandes séparées par “&”, “&&”, “|”, “||” ou “ ;”, si l'on veut que la redirection s'applique à l'ensemble des commandes, il faut employer des parenthèses (voir la partie “commandes composées”). L'entrée standard (stdin) peut être redirigée afin de lire dans un fichier plutôt qu'au clavier. Pour cela, placez “< fichier” en fin de commande. Exemple : pour lire la première ligne d'un fichier luser@localhost$ read < mon_script; echo $REPLY #! /bin/sh luser@localhost$ La sortie standard (stdout) peut être redirigée afin d'écrire dans un fichier plutôt qu'à l'écran. Pour cela, placez “> fichier” ou “» fichier” en fin de commande. Si l'opérateur “»” est utilisé, le contenu actuel du fichier est conservé et le texte est ajouté en fin de fichier. Exemple : Pour ajouter une ligne à la fin d'un fichier : toto> echo “FIN DU FICHIER” » fichier toto> La sortie standard d'erreur (stderr) peut être redirigée afin d'écrire dans un fichier plutôt qu'à l'écran. Pour cela, placez “2> fichier” en fin de commande. Exemple : Pour supprimer les messages d'erreur de l'affichage : ./ma_commande 2> /dev/null La sortie standard et la sortie standard d'erreur peuvent être redirigées ensemble afin d'écrire dans un fichier plutôt qu'à l'écran. Pour cela, placez “&> fichier” en fin de commande. Exemple : Pour qu'une commande n'affiche rien, même pas les erreurs ma_commande &> /dev/null Exemple : Pour afficher un message sur stderr plutôt que sur stdout : echo “Erreur fatale, le disque est plein!” 1>&2 en ajoutant “2>&1” à la fin de la commande. Exemple : Pour afficher le résultat d'une commande page par page, y compris les erreurs : ma_commande 2>&1 | more L'entrée standard peut être lue depuis le shell-script en ajoutant “< Exemple : Pour afficher plusieurs lignes de texte sans utiliser echo à chaque fois : cat « FIN_DU_TEXTE Vous êtes l'utilisateur $USER. Vous êtes situé dans le répertoire $(pwd). Merci de votre visite. FIN_DU_TEXTE La redirection par tube et l'emploi de l'expression “$(xxx)” ont déjà été décrits dans la partie “Commandes composées”.

Variables et paramètres

Variables

Contrairement au DOS qui ne connaît que les variables d'environnement, les shells d'Unix font la distinction entre variables simples et variables d'environnement. La différence entre les deux est que seules les variables d'environnement sont transmises aux programmes lancés depuis le shell (exception : lorsqu'on utilise le tube ou les parenthèses dans une commande composée, toutes les variables sont communiquées aux sous-shells qui sont lancés implicitement - par contre, ce qui suit reste valable). La modification d'une variable n'est perçue que par le shell qui la modifie et par ses descendants (c'est-à-dire les commandes qu'il exécute) lorsque c'est une variable d'environnement. Il s'ensuit qu'un script ne pourra jamais modifier les variables du shell courant, sauf s'il est appelé avec la commande source (voir plus haut “Invocation d'un script”). En Bourne-shell, définir une variable se fait par la commande : variable=“valeur”. Il est important que valeur soit spécifiée entre guillemets ou apostrophes, et qu'il n'y ait d'espaces ni avant ni après le signe '='. Si valeur est absente, la variable est créée mais contient une chaîne vide. L'utilisation d'une variable se fait grâce à l'expression $variable ou ${variable}. La deuxième notation est nécessaire si la variable est suivie d'un texte qui pourrait être interprété comme faisant partie du nom de la variable. Exemple :

luser@localhost$ jour=10; mois=11; an=1997; heures=19; minutes=30 luser@localhost$ echo “Nous sommes le $jour/$mois/$an” Nous sommes le 10/11/1997 luser@localhost$ echo “Il est ${heures}h ${minutes}mn” Il est 19h 30mn Définir une variable d'environnement se fait par la commande “export variable=valeur”. Transformer une variable en variable d'environnement se fait par la commande “export variable”. Afficher la liste des variables d'environnement se fait par la commande “export”. Afficher la liste complète des variables se fait par la commande “set”. La commande “unset var” permet de détruire définitivement la variable var. Au lancement du shell, beaucoup de variables sont prédéfinies, qu'elle soient d'environnement ou non. Par exemple, USER contient le nom de l'utilisateur, HOME contient le chemin de son répertoire personnel, etc… Pour en avoir la liste complète, faites “set” ou “export” dès le démarrage du shell, ou consultez le manuel en ligne de sh (“man sh” ou “man bash”). Sous Bourne-shell, certaines variables ont un sens spécial : - $$ donne le numéro de processus (pid) du shell. - $! donne le numéro de processus (pid) de la dernière commande lancée en tâche de fond (c'est-à-dire avec l'opérateur '&'). - $? donne la valeur retournée par la dernière commande. - $ donne la liste des options avec lesquelles le shell a été appelé.

Paramètres

La variable $# donne le nombre de paramètres accompagnant l'appel du shell-script. Les variables $@ et $* donnent l'ensemble des paramètres. La différence entre les deux termes ne s'observe que lorsqu'ils sont donnés entre guillemets. En effet, “$*” correspond à une seule valeur contenant tous les paramètres alors que “$@” correspond à autant de valeurs qu'il y a de paramètres. On peut accéder à chaque paramètre en spécifiant son numéro après le signe $. Si le numéro tient sur deux chiffres ou plus, il doit être donné entre accolades. Exemples : $1, …, $9, ${10}, ${11}, …\ Le paramètre $0 correspond au nom complet (avec le chemin) du shell- script. La commande “shift N” élimine les N premiers paramètres de la liste des paramètres (N ne doit pas être supérieur au nombre de paramètres). La variable $# est diminuée d'autant. Après une commande shift, les paramètres sont donc décalés, c'est à dire que $1 prend la valeur de $(N1)+, $2 prend la valeur de $(N2)+, etc… Exemple : #! /bin/sh # # params # # Shell-script de mise en application des paramètres d'un script. # Affiche l'ensemble des paramètres passés au script. echo “Vous avez lancé le shell-script $0” echo “Vous lui avez fourni $# paramètres qui sont: $*” echo ”“ echo “Liste des paramètres :” echo ”———————-“ numParam=1 for parametre in “$@”; do echo “Paramètre $numParam = $parametre” let $[numParam += 1] done echo ”“ echo -n “Après la commande 'shift $#', ” shift $# echo “il reste $# paramètres” Test : luser@localhost$ ./params A BC DEF “GH IJ” Vous avez lancé le shell-script en lui fournissant les 4 paramètres qui sont: A BC DEF GHIJ: luser@localhost$./params A BC DEF GHIJ Liste des paramètres:


Paramètre 1 = A Paramètre 2 = BC Paramètre 3 = DEF Paramètre 4 = GH IJ Après la commande 'shift 4', il reste 0 paramètres luser@localhost$

Calcul mathématique

Le shell permet d'effectuer des calculs mathématiques mais uniquement sur des entiers. Il y a deux syntaxes possibles : let $[ expression mathématique ] let $1) L'expression mathématique peut faire intervenir n'importe quelle variable (pas forcément précédée du signe $) ainsi que n'importe quel nombre entier décimal (219), hexadécimal (0xDB ou 16#DB), octal (0333 ou 8#333) ou binaire (2#011011011). Tous les opérateurs du langage C sont autorisés (+, -, *, %, ||, », =, +=, ^=, ==, !=, etc…), y compris les parenthèses. On peut aussi utiliser la formulation “commande $[ expression mathématique ]”. Dans ce cas, l'expression mathématique est remplacée par sa valeur puis la commande est exécutée. Exemple : luser@localhost$ echo $[ b % a ] 3 luser@localhost$ echo $[ a != 0 ] 1 luser@localhost$ echo $[ b > 20 ] 0 luser@localhost$ let $[ c = ( ( b + a ) « 4 ) & 0xFF ] luser@localhost$ echo $c 144 luser@localhost$

Les structures conditionnelles

Les conditions

La condition d'exécution des structures conditionnelles qui suivent est qu'une commande aboutisse, c'est à dire que sa valeur de retour soit 0. La commande qui sert de condition peut être une commande composée, en particulier on utilise souvent une formule du type “commande1 && commande2” ou “commande1 || commande2”. La condition peut être inversée si on fait précéder la commande d'un point d'exclamation. Donc ” ! commande“ signifie que la condition est vraie si la commande échoue. Attention, le point d'exclamation s'applique uniquement à la commande qui le suit ; pour qu'il s'applique à une commande composée, il faut l'encadrer avec des accolades (ou des parenthèses). Exemples : if commande; then echo “La commande a fonctionné”; fi if ! commande; then echo “La commande a échoué”; fi if commande1 && ! commande2; then
echo “commande1 a fonctionné mais pas commande2”; fi if ! { commande1 || commande2; } ; then \ echo “Ni commande1 ni commande2 n'ont fonctionné”; fi

Les tests

La condition d'exécution devant être une commande, les tests s'effectuent par l'intermédiaire de la commande “test expression” qui peut être abrégée par la formule ”[expression ]“. Les tests possibles peuvent porter sur des entiers, des chaînes de caractères ou des fichiers/répertoires. Tests sur les fichiers (et sur les répertoires) ”-e fichier“ : vrai si le fichier/répertoire existe. ”-s fichier“ : vrai si le fichier à une taille supérieure à 0. ”-r fichier“ : vrai si le fichier/répertoire est lisible. ”-w fichier“ : vrai si le fichier/répertoire est modifiable. ”-x fichier“ : vrai si le fichier est exécutable ou si le répertoire est accessible. ”-O fichier“ : vrai si le fichier/répertoire appartient à l'utilisateur. ”-G fichier“ : vrai si le fichier/répertoire appartient au groupe de l'utilisateur. ”-b nom“ : vrai si nom représente un périphérique (pseudo-fichier) de type bloc (disques et partitions de disques généralement). ”-c nom“ : vrai si nom représente un périphérique (pseudo-fichier) de type caractère (terminaux, modems et port parallèles par exemple). ”-d nom“ : vrai si nom représente un répertoire. ”-f nom“ : vrai si nom représente un fichier. ”-L nom“ : vrai si nom représente un lien symbolique. ”-p nom“ : vrai si nom représente un tube nommé. “fichier1 -nt fichier2” : vrai si les deux fichiers existent et si fichier1 est plus récent que fichier2. “fichier1 -ot fichier2” : vrai si les deux fichiers existent et si fichier1 est plus ancien que fichier2. “fichier1 -ef fichier2” : vrai si les deux fichiers représentent un seul et même fichier.

Tests sur les entiers

Il y a erreur si ce ne sont pas des entiers autour de l'opérateur. “entier1 -eq entier2” : vrai si entier1 est égal à entier2. “entier1 -ge entier2” : vrai si entier1 est supérieur ou égal à entier2. “entier1 -gt entier2” : vrai si entier1 est strictement supérieur à entier2. “entier1 -le entier2” : vrai si entier1 est inférieur ou égal à entier2. “entier1 -lt entier2” : vrai si entier1 est strictement inférieur à entier2. “entier1 -ne entier2” : vrai si entier1 est différent de entier2.

Tests sur les chaines

Encadrer la chaîne par des guillemets ! ”-n “chaîne”“ : vrai si la chaîne n'est pas vide. ”-z “chaîne”“ : vrai si la chaîne est vide. ”“chaine1” = “chaine2”“ : vrai si les deux chaînes sont identiques. ”“chaine1” != “chaine2”“ : vrai si les deux chaînes sont différentes.

Combinaison de tests

Lorsqu'un test doit être inversé ou lorsque plusieurs tests doivent être combinés par des ET et des OU, on a le choix entre deux notations. La première a été présentée dans la partie précédente (“Les conditions”) ; elle est préférable car elle est plus lisible. La deuxième fait partie de la syntaxe de la commande test : “NON expr” se traduit par “test ! expr” ou ”[ ! expr ]“. “expr1 OU expr2” se traduit par “test expr1 -o expr2” ou ”[ expr1 -o expr2 ]“. “expr1 ET expr2” se traduit par “test expr1 -a expr2” ou ”[ expr1 -a expr2 ]“.

Structure Si...Alors...Sinon

En Bourne-shell, la structure algorithmique se traduit par : Si commande1 aboutit, alors if commande1; then action1 Sinon si commande2 aboutit, alors elif commande2; then action2 Sinon else action3 FinSi fi Bien sûr, chacune des parties “sinon si” et “sinon” est optionnelle. D'autre part les actions à effectuer peuvent être composées de plusieurs commandes sur une ou plusieurs lignes. A l'extrême, on peut tout placer sur une seule ligne (bien respecter les points-virgules) mais c'est fortement déconseillé car cela nuit à la lisibilité : if commande1; then action1; elif commande2; then action2; else action3; fi Exemple : #! /bin/sh # # signe # # Shell-script de mise en application de l'instruction if…then…else. # Affiche le signe d'un nombre et de son opposé. # # Saisie de n # echo -n “Entrez un nombre: ” read n # # Test du signe de n # if test $n -lt 0; then # n < 0 ? echo “Le nombre $n est négatif” elif test $n -gt 0; then # n > 0 ? echo “Le nombre $n est positif” else # n = 0 echo “Le nombre $n est nul” fi # # Changement du signe de n # let $[ n = -n ] echo “Le nombre choisi devient $n” # # Test du signe de n (autre méthode) # if [ $n -le 0 ]; then # n ⇐ 0 ? if [ ! $n -eq 0 ]; then # n != 0 ? C'est à dire n < 0 ? echo “Le nombre $n est négatif” else # n = 0 echo “Le nombre $n est nul” fi else # n > 0 echo “Le nombre $n est positif” fi Test : luser@localhost$ ./signe Entrez un nombre: 3 Le nombre 3 est positif Le nombre choisi devient -3 Le nombre -3 est négatif luser@localhost$ ./signe Entrez un nombre: 0 Le nombre 0 est nul Le nombre choisi devient 0 Le nombre 0 est nul luser@localhost$

Structure Répéter...Jusqu'à

Cette structure n'existe pas en Bourne-shell. Attention aux confusions, le mot-clé until est lié à une structure TantQue, comme on le voit dans ce qui suit.

Structure TantQue

En Bourne-shell, la structure algorithmique se traduit par : TantQue commande aboutit while commande; do action action FinTQ done TantQue commande échoue until commande; do action action FinTQ done L'action à effectuer peut être composée de plusieurs commandes sur une ou plusieurs lignes. A l'extrême, on peut tout placer sur une seule ligne (bien respecter les points-virgules) mais c'est fortement déconseillé car cela nuit à la lisibilité : while commande; do action; done until commande; do action; done Exemple pour while : #! /bin/sh # # factorielle # # Shell-script de mise en application de l'instruction while. # Affiche la factorielle du nombre donné en paramètre. if [ $# -ne 1 ] || [ $1 -lt 0 ]; then echo “Usage: factorielle n (avec n >= 0)” 1>&2 else resultat=1 n=$1 while [ $n -gt 1 ]; do let $[ resultat *= n] let $[ n -= 1] done echo “$resultat” fi Test: luser@localhost$ factorielle 3 6 luser@localhost$ factorielle 6 720 luser@localhost$ factorielle $(factorielle 3) 720 Exemple : pour until #! /bin/sh # # testfact # # Shell-script de mise en application de l'instruction until. # Teste le shell-script de calcul de factorielles. n=0 until [ $n -eq 14 ]; do resultat=$(factorielle $n) echo “$n! = $resultat” let $[ n += 1 ] done Test: luser@localhost$ ./testfact 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 11! = 39916800 12! = 479001600 13! = 1932053504 luser@localhost$

Autres structures exécutives

Structure Pour

Pour chaque élément de l'ensemble (element1, element2, …, elementN) action sur element FinPour En Bourne-shell, la structure algorithmique se traduit par : for element in element1 element2 … elementN; do action $element done L'action à effectuer peut être composée de plusieurs commandes sur une ou plusieurs lignes. A l'extrême, on peut tout placer sur une seule ligne (bien respecter les points-virgules) mais c'est fortement déconseillé car cela nuit à la lisibilité :

for element in element1 element2 … elementN; do action $element; done Exemple : #! /bin/sh # # fic_ou_rep # # Shell-script de mise en application de l'instruction for. # Affiche le type (fichier ou répertoire) de chaque paramètre. for param in “$@”; do # Pour chaque paramètre… if [ -f “$param” ]; then # C'est un fichier? echo “$param est un fichier.” elif [ -d “$param” ]; then # C'est un répertoire? echo “$param est un répertoire.” elif ! [ -e “$param” ]; then # N'existe pas echo “$param n'existe pas.” else # C'est autre chose? echo “$param n'est ni un fichier, ni un répertoire.” fi done

Test : luser@localhost$ ./fic_ou_rep XXX /etc ”./un rep/“ /etc/hosts ”./un rep/un fic“ /dev/lp0 XXX n'existe pas. /etc est un répertoire. ./un rep/ est un répertoire . /etc/hosts est un fichier. ./un rep/un fic est un fichier . /dev/lp0 n'est ni un fichier, ni un répertoire. luser@localhost$ Remarque : sous Unix, les espaces sont des caractères légaux (mais pas conseillés) dans les noms de fichiers et de répertoires. Sans les guillemets dans le script et sur la ligne de commande, ”./un rep/“ aurait été interprété comme deux mots distincts : ”./un“ et “rep/”

Structure Selon

En Bourne-shell, la structure algorithmique se traduit par : Selon valeur case valeur in cas X : X ) action1 action1;; cas Y : cas Z : Y $|$ Z ) action2 action2;; defaut : * ) action3 action3;; FinSelon esac

Les actions à effectuer peuvent être composées de plusieurs commandes sur une ou plusieurs lignes. Pour chaque cas, la série de commandes à exécuter doit être terminée par deux points-virgules que l'on placera de préférence sur une seule ligne. Les cas à tester peuvent employer les caractères génériques autorisés pour les fichiers. Par exemple, les cas “a ? ? ?b”, “A*B*C” et [a-z]*[0-9] sont tout à fait valides. Si toutefois un caractère fait partie d'un cas à tester, il faudra l'encadrer par des guillemets pour qu'il ne soit pas interprété. Exemple : #! /bin/sh # # question # # Shell-script de mise en application de l'instruction case. # Pose une question et attend une réponse par oui ou non. # La valeur retournée est 0 pour oui et 1 pour non. retour=X while [ “$retour” = “X” ]; do echo -n “On continue (O/N) ? ” read reponse case “$reponse” in o* | O* ) retour=0 ;; [nN]* ) retour=1 ;; ”?“* ) echo “Il n'y a pas d'aide disponible” ;; * ) echo “Erreur : entrez [O]ui ou [N]on.” ;; esac echo ”“ done exit $retour Test : luser@localhost$ question ; echo “Retour = $?” On continue (O/N) ? bof Erreur: entrez [O]ui ou [N]on . On continue (O/N) ? oui, je veux bien Retour = 0 luser@localhost$ question ; echo “Retour = $?” On continue (O/N) ? ??? Il n'y a pas d'aide disponible On continue (O/N) ? Non, merci Retour = 1 luser@localhost$

Structure lorsque

Grâce à la commande trap, on peut déclencher une action sur un événement particulier. La contrainte est que cet événement soit un signal que l'on peut intercepter (faire “trap -l” pour en avoir la liste et “man 7 signal” pour obtenir des détails supplémentaires sur ces signaux). En Bourne-shell, la structure algorithmique se traduit par : Lorsque le signal SIGNAL survient trap “action” SIGNAL action FinLorsque L'action peut être une commande composée. Dès qu'elle ne se limite pas à un seul mot, elle doit être précisée entre guillemets. Les guillemets et apostrophes situés à l'intérieur doivent être précédés par un anti-slash. Bien sûr, plusieurs signaux peuvent être précisés en une seule fois. La commande trap utilisée seule donne la liste des actions exécutées pour chaque signal. La commande “trap SIGNAL” annule l'action à exécuter à l'arrivée du signal SIGNAL. Exemple : #! /bin/sh # # exTrap # # Shell-script de mise en application de l'instruction case. trap “echo Le script s\'est terminé” EXIT trap “echo Vous avez appuyé sur Ctrl-C” SIGINT trap “echo Vous avez fait: kill $$” SIGTERM trap “echo J\'ai été stoppé et je continue” SIGCONT trap “echo J\'ai rec?u SIGUSR1 ou SIGUSR2” SIGUSR1 SIGUSR2 echo “Processus $$: J'attend un signal…” # # Compte à rebours # i=10 while [ $i -gt 0 ]; do echo -n “$i ” sleep 1 let $[ i -= 1 ] done echo “0” Test : luser@localhost$ ./exTrap Processus 1658: J'attend un signal… 10 9 Vous avez appuyé sur Ctrl-C 8 7 [1]+ Stopped exTrap luser@localhost$ kill -SIGUSR1 1658 luser@localhost$ fg exTrap J'ai reçu SIGUSR1 ou SIGUSR2 J'ai été stoppé et je continue 6 5 4 [1]+ Stopped exTrap luser@localhost$ kill -SIGUSR2 1658 luser@localhost$ kill 1658 luser@localhost$ fg exTrap J'ai reçu SIGUSR1 ou SIGUSR2 Vous avez fait: kill 1658 J'ai été stoppé et je continue 3 2 1 0 Le script s'est terminé luser@localhost$

Fonctions

Le Bourne-shell offre la possibilité de définir ses propres fonctions. Bien qu'elles soient internes à un shell, elles s'emploient comme un script externe. Mais leur appel ne provoque pas le lancement d'un sous-shell, donc une fonction a accès à toutes les variables, pas seulement celles d'environnement, et leur modification reste prise en compte lorsque la fonction se termine. Syntaxe function maFonction() { local var1 local var2=“valeur” commande1 commande2 return val; } Le mot-clé “local” permet de définir des variables locales à la fonction. La dernière commande doit être terminée par un point-virgule. On accède aux paramètres d'une fonction comme à ceux d'un script : grâce aux variables $*, $@, $#, $1, $2, … qui sont temporairement modifiées pendant toute la durée de la fonction. En revanche, $0 ne change pas. Le retour d'une valeur s'effectue grâce au mot-clé return. Si return n'est pas employé, la valeur de retour est celle de la dernière commande exécutée. Attention, l'emploi de la commande exit termine non seulement la fonction mais aussi le script. Une fonction peut être récursive, c'est àdire qu'elle peut s'appeler elle-même. Une fois définie, une fonction apparaît dans la liste des variables. On peut l'exporter vers les autres shells grâce à la commande “export -f maFonction”. Exemple : #! /bin/sh # # signe2 # # Shell-script de mise en application des fonctions. # Affiche le signe d'un nombre et de son carré. # # # carré(nombre) # # Retourne le carré du nombre donné en paramètre. # # Paramètres: $1 → Le nombre dont on veut le carré # function carré() { return $[ $1 * $1 ] } # # # affSigne(nombre) # # Affiche le signe du nombre donné en paramètre. # # Paramètres: $1 → Le nombre dont on teste le signe. # function affSigne() { local signe if [ $n -lt 0 ]; then # n < 0 ? signe=négatif elif [ $n -gt 0 ]; then # n > 0 ? signe=positif else # n = 0 signe=nul fi echo “Le nombre $1 est $signe” } # # # Programme principal # echo -n “Entrez un nombre: ” read n # Saisie de n affSigne $n # Affichage du signe de n carré $n # Calcul du carré de n n=$? # Récupération du résultat echo “Le carré du nombre choisi est $n” affSigne $n # Affichage du signe de n

Test : luser@localhost$ ./signe2 Entrez un nombre: -5 Le nombre -5 est négatif Le carré du nombre choisi est 25 Le nombre 25 est positif luser@localhost$ ./signe2 Entrez un nombre: 0 Le nombre 0 est nul Le carré du nombre choisi est 0 Le nombre 0 est nul

<note important>Page en cours de création, mise en page non terminé.</note>

1)
expression mathématique
informatique/linux/ecriture_de_script_ecriture_de_scripts_bash.txt · Dernière modification : 2022/04/10 17:26 de 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki