Le shell permet de définir ou redéfinir des variables pour l'environnement de l'utilisateur.
Il est également possible de définir d'autres variables dites variables utilisateur qui vont permettre de stocker des informations utiles durant l'exécution de scripts.
$ variable1=valeur1
$ echo $variable1
valeur1
$
Il ne faut surtout pas mettre d'espace avant et après le signe égal (=) ainsi que dans la valeur de la variable.
Si un espace est placé avant le signe '=', le shell va interpréter la chaine 'variable1' comme étant une commande et la chaine '=valeur1' comme étant un paramètre de la commande variable1.
Pour pouvoir affecter une valeur contenant un espace, il faut obligatoirement le protéger avec des simples quotes.
$ variable2='valeur2 valeur21'
$ echo $variable2
valeur2 valeur21
$
Pour supprimer le contenu d'une variable, il faut utiliser la commande unset :
$ variable3=valeur3
$ echo $variable3
valeur3
$ set | grep variable3
variable3=valeur3
$ unset variable3
$ echo $variable3
$ set | grep variable3
$
Concaténer des variables avec une chaine de caractères :
Un exemple qui produit une erreur
$ fichier=monFichier
$ dateJour=20111006
$ nouveauFichier=$fichier_$dateJour
$ echo $nouveauFichier
20111006
$
Le but de cet exemple est de concaténer la variable $fichier, un tiret bas '_', puis la variable $dateJour.
Le résultat final est erroné car au moment d'affecter la nouvelle variable nouveauFichier (ligne 3), le shell interprète $fichier_ comme étant une variable à part entière puisque le tiret bas '_' est autorisé dans le nommage des variables.
La variable $fichier_ n'existant pas, celle ci est égale à "blanc".
Pour indiquer au shell qu'il faut interpréter le tiret bas '_' comme étant une chaine de caractères, il faut obligatoirement entourer la variable qui le précède avec des {}.
$ fichier=monFichier
$ dateJour=20111006
$ nouveauFichier=${fichier}_$dateJour
$ echo $nouveauFichier
monFichier_20111006
$
Substituer des variables :
Le shell offre la possibilité d'attribuer une valeur par défaut aux variables non initialisées ou au contraire initialisées.
Expression ${variable:-valeur}
$ jour=mardi
$ echo "Nous sommes : ${jour:-dimanche}"
Nous sommes : mardi
$ unset jour
$ echo "Nous sommes : ${jour:-dimanche}"
Nous sommes : dimanche
$ echo $jour
$
Expression ${variable:=valeur}
$ jour=mardi
$ echo "Nous sommes : ${jour:=dimanche}"
Nous sommes : mardi
$ echo $jour
mardi
$ unset jour
$ echo "Nous sommes : ${jour:=dimanche}"
Nous sommes : dimanche
$ echo $jour
dimanche
$
Expression ${variable:+valeur}
$ bool=1
$ echo "Si bool=1 alors j'affiche : ${bool:+true}"
Si bool=1 alors j'affiche : true
$ unset bool
$ echo "Si bool=1 alors j'affiche : ${bool:+true}"
Si bool=1 alors j'affiche :
$
Expression ${variable:?message}
Dans un script, si la variable est vide, le message est affiché et le script se termine aussitôt
$ nombre=56
$ echo ${nombre:?"nombre indefini"}
56
$ unset nombre
$ echo ${nombre:?"nombre indefini"}
-bash: nombre: nombre indefini
Affichage du message par défaut
$ echo ${nombre:?}
-bash: nombre : parametre vide ou non defini
$
Les caractères de substitution permettent de remplacer une commande par le résultat de son exécution.
Par exemple, comment faire pour afficher un message affichant la date du jour.
$ echo "Nous sommes le date"
Nous sommes le date
$
Il suffit d'entourer la commande avec les caractères de substitution :
$ echo "Nous sommes le `date`"
Nous sommes le vendredi 7 octobre 2011, 08:39:49 (UTC+0200)
$ echo "Nous sommes le $(date)"
Nous sommes le vendredi 7 octobre 2011, 08:40:05 (UTC+0200)
$
Idem pour initialiser une variable :
$ date=`date`
$ echo $date
vendredi 7 octobre 2011, 08:42:34 (UTC+0200)
$
Les caractères de protection servent à utiliser les caractères spéciaux du shell comme n'importe quels caractères.
Les simples quotes désactivent tous les caractères spéciaux du shell. Ils doivent être utilisés en nombre pair sur une ligne de commande.
$ echo $PWD
/home/toto
Avec les simples quotes, la fonction du caractère '$' est désactivée :
$ echo '$PWD'
$PWD
$
Afficher tous les fichiers d'un dossier à l'aide de la fonction 'echo' et du caractère spécial '*'
$ echo *
1coucou cOucou Coucou coucou.r ...
Avec les simples quotes, la fonction du caractère '*' est désactivée :
$ echo '*'
*
$
$ echo Bonjour $(logname)
Bonjour toto
$ echo 'Bonjour $(logname)'
Bonjour $(logname)
$
La quote ne se protège pas elle-même.
Tant que les quotes ne sont pas par pair, le shell affiche le prompt secondaire
$ echo 'J'affiche un quote supplémentaire'
>
L'antislash désactive la fonctionnalité du caractère qui le suit.
$ echo La liste des fichiers * du dossier $PWD
La liste des fichiers 1coucou cOucou Coucou coucou.r du dossier /volume1/home/ronan/atuer/toto
$ echo La liste des fichiers \* du dossier \$PWD
La liste des fichiers * du dossier $PWD
$
L'antislash se protège lui-même
$ echo \\
\
$
$ echo L\'antislash protège la quote
L'antislash protège la quote
$
Le premier antislash protège le second, le troisième protège le '$'
$ echo \\\$PWD
\$PWD
$
Les guillemets protège tous les caractères spéciaux du shell à l'exception du dollar $, des quotes inversés ``, du dollar et des parenthèses $( ), de l'antislash \ et des guillemets ".
$ echo "
> La variable \$HOME est substituée par $HOME
> La commande \$(logname) est exécutée : $(logname)
> Je peux afficher des > et des |
> Et même des guillemets \"."
La variable $HOME est substituée par /home/toto
La commande $(logname) est exécutée : toto
Je peux afficher des > et des |
Et même des guillemets ".
$
En règle générale, il est préférable d'utiliser la commande echo avec des guillemets
Caractères | Signification |
---|---|
espace - tabulation - saut de ligne | Séparateurs de mots sur la ligne de commande |
& | Arrière-plan |
| << < > >> | Tubes et redirections |
() et {} | Regroupement de commandes |
; | Séparateur de commandes |
* ? [ ] ?( ) +( ) *( ) !( ) @( ) | Caractères de génération de noms de fichier |
$ et ${ } | Valeur d'une variable |
`` $( ) | Substitution de commandes |
' ' " " \ | Caractères de protection |
Les caractères spéciaux du shell sont interprétés dans un ordre bien précis.
$ echo "\$HOME --> $HOME
> Mon login --> $(logname)
> Nous sommes le `date`
> La liste des fichiers :" * | nl > test ; cat test
1 $HOME --> /home/toto
2 Mon login --> toto
3 Nous sommes le jeudi 20 octobre 2011, 08:27:05 (UTC+0200)
4 La liste des fichiers : fichier1 fichier2 fichier3 fichier4
$
Un script shell est un fichier texte contenant des commandes Linux internes ou externes.
Le nom du script peut avoir une extension mais cela n'est pas obligatoire.
En règle générale, tous les scripts portent l'extension .sh
Exemple :
$ nl monPremierScript.sh
1 pwd
2 echo "Liste des fichiers commençant par la lettre C"
3 ls -lh c*
$
Exécution :
$ bash monPremierScript.sh
/home/toto
Liste des fichiers commençant par la lettre C
-rw-r--r-- 1 toto toto 0 2011-09-14 13:55 cOucou
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.r
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.s
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.t
-rw-r--r-- 1 toto toto 0 2011-09-15 07:21 coucou.uvw
$
Il existe 3 méthodes pour exécuter un script.
La première méthode consiste à appeler la commande bash, sh, ksh etc.. (l'interpréteur de commande désiré) suivi du nom du script.
$ bash monPremierScript.sh
Dans ce cas, le droit de lecture sur le fichier est suffisant.
$ ls -l monPremierScript.sh
-rw-r--r-- 1 toto toto 68 2011-10-20 08:52 monPremierScript.sh
$
La seconde méthode consiste à utiliser l'entrée standard de la commande bash, sh, ksh etc.. afin de lui faire lire le contenu du script.
$ bash < monPremierScript.sh
Dans ce cas, le droit de lecture sur le fichier est également suffisant.
Cette méthode est très peu utilisée.
La troisième méthode consiste à rendre le script exécutable.
$ chmod u+x monPremierScript.sh
$ ls -l monPremierScript.sh
-rwxr--r-- 1 toto toto 68 2011-10-20 08:52 monPremierScript.sh
$ ./monPremierScript.sh
Choisir un interpréteur de commande :
Pour indiquer au script quel interpréteur de commande utiliser, il faut indiquer sur la première ligne du script les caractères #! suivi du chemin absolu de l'interpréteur.
Par exemple, pour utiliser l'interpéteur de commande BASH, on recherche son chemin absolu :
$ whereis bash
bash: /bin/bash /etc/bash.bashrc /usr/share/man/man1/bash.1.gz
$
La résultat de la commande nous indique le répertoire /bin/bash
Inscrire sur la première ligne du script #! /bin/bash
$ nl monPremierScript.sh
1 #! /bin/bash
2 pwd
3 echo "Liste des fichiers commençant par la lettre C"
4 ls -lh c*
$
De cette manière, le script sera toujours exécuté avec l'interpréteur de commande BASH.
L'exécution d'un script par le shell courant permet de modifier l'environnement du shell courant.
Les droits d'exécution sur le script ne sont pas nécessaires.
L'utilisation de la commande interne "." est nécessaire pour l'exécution.
Par exemple, pour modifier l'éditeur par défaut, il est nécessaire de modifier la variable environnement EDITOR.
$ env | grep EDITOR
EDITOR=/usr/bin/vi
$ echo $EDITOR
/usr/bin/vi
$ nano .profile
export EDITOR='/usr/bin/nano'
^o (enregistrer)
^x (quitter)
$ . .profile
$ echo $EDITOR
/usr/bin/nano
$ env | grep EDITOR
EDITOR=/usr/bin/nano
$
Attention, l'appel de la commande interne "." est obligatoirement suivi d'un espace et d'un script shell.
Il est possible d'écrire dans un script des commentaires afin de faciliter la compréhension du script.
Les commentaires sont désignés à l'aide du caractère #.
Ne pas confondre avec #! inscrit sur la première ligne du script qui sert à renseigner l'interpréteur de commande.
$ nl monPremierScript.sh
1 #! /bin/bash
2 # Ceci est un commentaire
3 # La commande PWD indique le répertoire courant
4 pwd
5 # La commande ECHO permet d'afficher du texte et des informations
6 echo "Liste des fichiers commençant par la lettre C"
7 # La commande LS permet de lister tous les fichiers commençant par la lettre "c"
8 ls -lh c*
$
Dans un script, il est possible de récupérer en lecture un certain nombre de variables réservées.
Ces variables sont initialisées par le shell et contiennent des informations diverses et variées.
Les scripts shell sont capables de récupérer des arguments placés juste après l'appel du script.
En KSH et en BASH, il est possible d'aller au delà de 9 arguments en utilisant les variables spéciales ${10}, ${11} etc...
Les accolades sont obligatoires à partir du moment où le nom de la variable contient plus d'un chiffre.
Exemple :
$ nano monSecondScript.sh
#! /bin/bash
echo "Il y a $# arguments passés au script"
echo "Le script se nomme $0"
echo "La valeur du premier argument : $1"
echo "La valeur du second argument : $2"
echo "La valeur du troisième argument : $3"
echo "Les valeurs de tous les arguments : $*"
^o (enregistrer)
^x (quitter)
$ chmod u+x monSecondScript.sh
$ ./monSecondScript.sh arg1 arg2 arg3 arg4 arg5
Il y a 5 arguments passés au script
Le script se nomme ./monSecondScript.sh
La valeur du premier argument : arg1
La valeur du second argument : arg2
La valeur du troisième argument : arg3
Les valeurs de tous les arguments : arg1 arg2 arg3 arg4 arg5
$
Exemple avec 3 arguments :
$ ./monSecondScript.sh 25 + 15
Il y a 3 arguments passés au script
Le script se nomme ./monSecondScript.sh
La valeur du premier argument : 25
La valeur du second argument : +
La valeur du troisième argument : 15
Les valeurs de tous les arguments : 25 + 15
$
Tous les arguments doivent être séparés les uns des autres par un espace ou une tabulation
La commande shift permet de décaler la liste des arguments d'une ou plusieurs positions vers la gauche.
Cette commande est utilisée dans le cas où le premier argument n'a pas à subir le même traitement que les suivants.
Dans ce cas, le premier argument doit être sauvegardé dans une variable et l'exécution de la commande shift entraine le remplacement du premier argument par le second et ainsi de suite.
$ nl monTroisiemeScript.sh
1 #! /bin/bash
2 # Ce script doit recevoir en premier argument le nom d'un dossier puis des noms de fichiers pour les suivants
3 # Affichage des variables avant l'utilisation de la commande shift
4 echo -e "Affichage avant exécution de la commande shift\n"
5 echo "1er argument \$1 : $1"
6 echo "1er argument \$2 : $2"
7 echo "1er argument \$3 : $3"
8 echo "1er argument \$4 : $4"
9 echo "Tous les arguments \$* : $*"
10 echo -e "Le nombre d'argument \$# : $#\n"
11 # On sauvegarde le premier argument dans la variable rep
12 rep=$1
13 # On décale tous les arguments avec la commande shift
14 shift
15 # Affichage des variables après l'exécution de la commande shift
16 echo -e "Affichage après exécution de la commande shift\n"
17 echo "1er argument \$1 : $1"
18 echo "1er argument \$2 : $2"
19 echo "1er argument \$3 : $3"
20 echo "1er argument \$4 : $4"
21 echo "Tous les arguments \$* : $*"
22 echo -e "Le nombre d'argument \$# : $#\n"
23 # Création du répertoire
24 mkdir $rep
25 # On se positionne dans le nouveau répertoire
26 cd $rep
27 # Création des fichiers dans le nouveau répertoire
28 for fichier in $*
29 do
30 touch $fichier
31 done
$ chmod u+x monTroisiemeScript
Exécution du script :
$ ./monTroisiemeScript.sh test fic1 fic2 fic3 fic4 fic5 fic6
Affichage avant exécution de la commande shift
1er argument $1 : test
1er argument $2 : fic1
1er argument $3 : fic2
1er argument $4 : fic3
Tous les arguments $* : test fic1 fic2 fic3 fic4 fic5 fic6
Le nombre d'argument $# : 7
Affichage après exécution de la commande shift
1er argument $1 : fic1
1er argument $2 : fic2
1er argument $3 : fic3
1er argument $4 : fic4
Tous les arguments $* : fic1 fic2 fic3 fic4 fic5 fic6
Le nombre d'argument $# : 6
$ ls -l ./test/
total 0
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic1
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic2
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic3
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic4
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic5
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic6
$
Toutes les commandes Linux retournent un code d'erreur compris entre 0 et 255.
La valeur 0 représente la valeur vrai (succès de la commande).
Les valeurs supérieur à 0 représente la valeur faux (échec de la commande).
Le code erreur de la dernière commande utilisée est contenu dans la variable $?
$ ls f*
f1chier fichier159159.log fichier159.log fichier161.log fichier1.abc fichier1.c fset
fichier159159159.log fichier159160.log fichier160.log fichier1.a fichier1.b fichier.log
$ echo $?
0
$ ls 2*
ls: impossible d'accéder à 2*: Aucun fichier ou dossier de ce type
$ echo $?
2
Dans un script shell, le test du code de retour d'une commande permet d'effectuer différentes actions.
Un script shell étant lui-même une commande, il est possible de lui faire retourner un code d'erreur avec la commande exit.
Par exemple, il est possible d'indiquer dans un script exit 1 afin d'indiquer une erreur rencontrée et/ou exit 0 afin d'indiquer que tout c'est bien déroulé.
Dans l'exemple suivant, le script test s'il reçoit bien au minimum 2 arguments et renvoi le code erreur 1 si c'est faux.
$ nl ./script.sh
1 #! /bin/bash
2 # Le script doit recevoir au minimum 2 arguments
3 if [ $# -lt 2 ]
4 then
5 # Si le nombre d'arguments est inférieur à 2
6 # on retourne le code erreur 1
7 echo "Nombre arguments incorrect"
8 exit 1
9 else
10 # Si le nombre d'arguments est supérieur ou égal à 2
11 # on retourne le code erreur 0
12 echo "Nombre arguments correct"
13 exit 0
14 fi
$
Exécution du script :
$ ./script.sh
Nombre arguments incorrect
$ echo $?
1
$ ./script.sh test
Nombre arguments incorrect
$ echo $?
1
$ ./script.sh test test2
Nombre arguments correct
$ echo $?
0
$
La variable $$ représente le PID du shell qui interprète le script.
La valeur de cette variable est la même pendant toute la durée d'exécution du script et différente à chaque utilisation du script.
Exemple :
Dans ce script, la variable $$ est utilisée pour générer le nom d'un dossier différent à chaque exécution du script.
$ nl monQuatriemeScript.sh
1 #! /bin/bash
2 dossierTemp=dossier_$$
3 echo "Création du dossier \"$dossierTemp\""
4 mkdir $dossierTemp
5 cd $dossierTemp
6 for (( i=0 ; i<10 ; i++)) do
7 touch fichier_$i
8 done
9 exit 0
$
$ ./monQuatriemeScript.sh
Création du dossier "dossier_26563"
$ ./monQuatriemeScript.sh
Création du dossier "dossier_26581"
$
La variable $! représente le PID d'une commande exécutée en arrière plan.
exemple :
$ nl monCinquiemeScript.sh
1 #! /bin/bash
2 echo "Le script est exécuté sous le PID $$"
3 find /etc -name $1 1> resultat 2> /dev/null &
4 echo "La commande FIND est en cours d'exécution sous le PID $!"
5 ps
6 exit 0
$
$ ./monCinquiemeScript.sh hosts.allow
Le script est exécuté sous le PID 29999
La commande FIND est en cours d'exécution sous le PID 30000
PID TTY TIME CMD
19703 pts/0 00:00:00 bash
29999 pts/0 00:00:00 monCinquiemeScr
30000 pts/0 00:00:00 find
30001 pts/0 00:00:00 ps
$
La commande read lit son entrée standard et affecte les valeurs saisies dans la ou les variables passées en argument.
$ read var1
coucou
$ echo $var1
coucou
$
Tous les mots saisis sont stockés dans la variables var1
$ read var1
coucou tout le monde
$ echo $var1
coucou tout le monde
$
Le premier mot est stocké dans var1 et le second dans var2
$ read var1 var2
A bientot
$ echo $var1
A
$ echo $var2
bientot
$
Le premier mot est stocké dans var1 et les autres dans var2
$ read var1 var2
A bientot tout le monde
$ echo $var1
A
$ echo $var2
bientot tout le monde
$
Le mot est stocké dans var1 et var2 est vide
$ read var1 var2
Bonjour
$ echo $var1
Bonjour
$ echo $var2
$
En KSH, il est possible d'associer un message à la commande read
$ read var1?"Saisir une valeur : "
Saisir une valeur : coucou
$ echo $var1
coucou
$
Le code retour de la commande read est vrai (0) si un texte est saisi et faux (1) si la commande est interrompue en appuyant sur ctrl+d (^d)
$ read var1
coucou
$ echo $?
0
$ echo $var1
coucou
$
$ read var1
[Entrée]
$ echo $?
0
$ echo $var1
$
$ read var1
$ ^d
$ echo $?
1
$ echo $var1
$
Cette variable contient les caractères qui permettent de scinder les entrées au clavier.
Par défaut elle contient les caractères espace, tabulation (\t) et saut de ligne (\n)
Le contenu de cette variable peut être modifié
$ OLDIFS="$IFS"
$ IFS="|" # modification du caractère de séparation
$ read var1 var2 var3
colonne1|colonne2|colonne3
$ echo $var1
colonne1
$ echo $var2
colonne2
$ echo $var3
colonne3
$ IFS="$OLDIFS"
$
Les expression $IFS et $OLDIFS doivent obligatoirement être placées entre guillemets pour que les caractères internes ne soient pas interprétés par le shell
En BASH, il est possible d'associer un message à la commande read grâce à l'option -p :
$ read -p "Votre nom ? " NAME
Votre nom ? toto
$ echo $NAME
toto
En y ajoutant l'option -s , il est possible d'utiliser la commande read pour saisir un mot de passe :
$ read -s -p "Votre passwd ? " PASSWD; echo
Votre passwd ?
$ echo $PASSWD
aaaa
Avec l'option -s, il ne faut pas oublier d'ajouter la commande echo pour afficher un saut de ligne.
La commande test permet de faire des tests sur des fichiers, des chaines de caractères et des nombres.
Elle renvoie le code retour 0 (vrai) ou 1 (faux) qu'il est possible de consulter en affichant la valeur de $?
Il existe 2 syntaxes pour utiliser la commande test
$ test expression
$ [ expression ]
La paire de crochet signifie la même chose que la commande test.
Les crochets ouvrants et fermants doivent obligatoirement être suivis et précédés d'un espace.
Expression | Code de retour |
---|---|
-b FILE | Vrai si le fichier existe et est du type spécial bloc |
-c FILE | Vrai si le fichier existe et est du type spécial caractère |
-d FILE | Vrai si le fichier existe et est du type répertoire |
-e FILE | Vrai si le fichier existe |
-f FILE | Vrai si le fichier existe et est du type ordinaire |
-G FILE | Vrai si le fichier existe et si l'utilisateur appartient au groupe propriétaire du fichier |
-h FILE | Vrai si le fichier existe et est du type lien symbolique |
-L FILE | Vrai si le fichier existe et est du type lien symbolique (idem -h) |
-O FILE | Vrai si le fichier existe et si l'utilisateur est le propriétaire du fichier |
-r FILE | Vrai si le fichier existe et est accessible en lecture |
-s FILE | Vrai si le fichier existe et n'est pas vide |
-S FILE | Vrai si le fichier existe et est du type socket |
-w FILE | Vrai si le fichier existe et est accessible en écriture |
-x FILE | Vrai si le fichier existe et est exécutable |
FILE1 -ef FILE2 | Vrai si les fichiers ont le même lien physique |
FILE1 -nt FILE2 | Vrai si FILE1 est plus récent que FILE2 |
FILE1 -ot FILE2 | Vrai si FILE1 est plus ancien que FILE2 |
Exemple :
Le fichier /etc/group est un fichier ordinaire
$ test -f /etc/group
$ echo $?
0
$
Le fichier /etc/groupe n'existe pas (test avec l'autre syntaxe)
$ [ -f /etc/groupe ]
$ echo $?
1
$
Le fichier /etc/init.d existe et est un répertoire
$ [ -d /etc/init.d ]
$ echo $?
0
$
Le fichier /etc/group n'est pas un répertoire ou il n'exsite pas
$ test -d /etc/group
$ echo $?
1
$
Le fichier contenu dans la variable file n'est pas un répertoire ou n'existe pas
$ file=/etc/group
$ test -d $file
$ echo $?
1
$
L'utilisateur n'a pas le droit d'écriture sur le fichier /etc/group
$ [ -w /etc/group ]
$ echo $?
1
$
Expression | Code de retour |
---|---|
-n STRING | Vrai si la longueur de la chaine n'est pas égale à 0 |
-z STRING | Vrai si la longueur de la chaine est égale à 0 |
STRING1 = STRING2 | Vrai si les 2 chaines sont égales |
STRING1 != STRING2 | Vrai si les 2 chaines sont différentes |
STRING | Vrai si la chaine n'est pas vide (idem -n) |
Pour les tests sur les chaines de caractères, il est recommandé de mettre le nom des variables entre guillemets.
Exemple :
Avec les variables suivantes :
$ str1="test1"
$ str2="test2"
$ str3="test1"
$
La variable $str1 n'est pas vide
$ [ -n "$str1" ]
$ echo $?
0
$
$ [ "$str1" ]
$ echo $?
0
$ [ -z "$str1" ]
$ echo $?
1
$
La variable $str4 est vide
$ [ -n "$str4" ]
$ echo $?
1
$
$ [ -z "$str4" ]
$ echo $?
0
$
$ [ "$str4" ]
$ echo $?
1
$
$str1 & $str3 sont identiques
$ [ "$str1" = "$str3" ]
$ echo $?
0
$
$ [ "$str1" != "$str3" ]
$ echo $?
1
$
$str1 & $str2 sont différentes
$ [ "$str1" != "$str2" ]
$ echo $?
0
$
$ [ "$str1" = "$str2" ]
$ echo $?
1
$
Expression | Code de retour |
---|---|
INT1 -eq INT2 | Vrai si INT1 est égal à INT2 (=) |
INT1 -ge INT2 | Vrai si INT1 est supérieur ou égal à INT2 (>=) |
INT1 -gt INT2 | Vrai si INT1 est supérieur à INT2 (>) |
INT1 -le INT2 | Vrai si INT1 est inférieur ou égal à INT2 (<=) |
INT1 -lt INT2 | Vrai si INT1 est inférieur à INT2 (<) |
INT1 -ne INT2 | Vrai si INT1 est différent de INT2 (!=) |
Exemple :
Avec les variables suivantes
$ int1=1
$ int2=2
$ int3=3
$ int4=2
$
$int2 & $int4 sont égaux
$ [ $int2 -eq $int4 ]
$ echo $?
0
$int2 est supérieur ou égal à $int4
$ [ $int2 -ge $int4 ]
$ echo $?
0
$int3 est supérieur ou égal à $int1
$ [ $int3 -ge $int1 ]
$ echo $?
0
$int4 n'est pas supérieur ou égal à $int3
$ [ $int4 -ge $int3 ]
$ echo $?
1
$int3 est supérieur à $int2
$ [ $int3 -gt $int2 ]
$ echo $?
0
$int2 n'est pas supérieur à $int3
$ [ $int2 -gt $int3 ]
$ echo $?
1
$int2 est inférieur ou égal à $int3
$ [ $int2 -le $int3 ]
$ echo $?
0
$int2 est inférieur à $int3
$ [ $int2 -lt $int3 ]
$ echo $?
0
$int2 est inférieur ou égal à $int4
$ [ $int2 -le $int4 ]
$ echo $?
0
$int2 est différent de $int3
$ [ $int2 -ne $int3 ]
$ echo $?
0
$int2 n'est pas différent de $int4
$ [ $int2 -ne $int4 ]
$ echo $?
1
$
Opérateur | Signification |
---|---|
! | Négation |
-a | ET |
-o | OU |
Les opérateurs sont exécutés avec une priorité bien précise :
Le fichier /etc/group n'est pas un répertoire
$ [ ! -d /etc/group ]
$ echo $?
0
$
Le fichier monPremierScript.sh existe et est exécutable
$ [ -f monPremierScript.sh -a -x monPremierScript.sh ]
$ echo $?
0
$
Le fichier monPremierScript.sh n'est pas un répertoire mais il est exécutable
$ [ -d monPremierScript.sh -o -x monPremierScript.sh ]
$ echo $?
0
$
Il est possible de modifier la priorité d'exécution des opérateurs en utilisant des paranthèses.
\(........\)
Les parenthèses doivent être protégées par des antislashes afin de ne pas être interprétées par le shell comme étant un regroupement de commandes.
L'utilisateur doit avoir le droit d'écriture sur le fichier fic1 et le fichier fic4 ou fic7 doit exister
$ ls
fic1 fic2 fic3 fic4 fic5 fic6 script.sh
$ [ -w fic1 -a \( -e fic4 -o -e fic7 \) ]
$ echo $?
0
$
Il doit toujours y avoir un espace autour des opérateurs !, -a et -o.
Utilisation de la commande test avec la structure de controle if.
Principe d'utilisation :
if commande1
then
commande2
commande3
...
else
commande4
...
fi
if
commande2
commande3
...
else
commande4
...
fi
La commande1 est exécutée, si son code retour ($?) vaut 0 (vrai) alors les commandes 2 & 3 sont exécutées, sinon c'est la commande4 qui est exécutée (code retour de la commande1 supérieur à 0 - faux).
Exemple :
Dans l'exemple suivant, le script test s'il reçoit bien au minimum 2 arguments.
Dans le cas contraire, le script affiche un message indiquant que le nombre d'arguments est incorrect et se termine en retournant un code erreur 1.
$ nl monTroisiemeScript.sh
1 #! /bin/bash
2 # Ce script doit recevoir en premier argument le nom d'un dossier puis des noms de fichiers pour les suivants
4 # Ce script doit recevoir au minimum 2 arguments
5 # Le premier étant le nom d'un dossier
6 # Les suivants étant les noms des fichiers
7 # On test si il y a 2 arguments au minimum et on retourne le code erreur 1 si c'est faux
8 if [ $# -lt 2 ]
9 then
10 echo "Nombre d'arguments incorrect !!!"
11 exit 1
12 fi
13 # On sauvegarde le premier argument dans la variable rep
14 rep=$1
15 # On décale tous les arguments avec la commande shift
16 shift
17 # Création du répertoire
18 mkdir $rep
19 # On se positionne dans le nouveau répertoire
20 cd $rep
21 # Création des fichiers dans le nouveau répertoire
22 for fichier in $*
23 do
24 touch $fichier
25 done
26 exit 0
$ ./monTroisiemeScript.sh test
Nombre d'arguments incorrect !!!
$ echo $?
1
$
$ ./monTroisiemeScript.sh test3 coucou
$ echo $?
0
$
La commande [[ ]] est une version améliorée de la commande test.
Tous les opérateurs utilisés avec la commandes test restent valables à l'exception des opérateurs logiques -a et -o respectivement remplacés par && et ||.
Contrairement à la commande test, il n'est plus nécessaire d'entourer les variables avec des guillemets.
Les différentes syntaxes utilisables :
$ echo $null
$ test -z "$null"
$ echo $?
0
$ [ -z "$null" ]
$ echo $?
0
$ [[ -z $null ]]
$ echo $?
0
$ [[ -z "$null" ]]
$ echo $?
0
$ [[ $null = "" ]]
$ echo $?
0
$
Les opérateurs suivants ont été ajoutés :
Opérateurs | Code de retour |
---|---|
$chaine = modele | Vrai si $chaine correspond au modèle |
$chaine != modele | Vrai si $chaine ne correspond pas au modèle |
$chaine1 < $chaine2 | Vrai si $chaine1 est lexicographiquement avant $chaine2 |
$chaine1 > $chaine2 | Vrai si $chaine1 est lexicographiquement après $chaine2 |
En utilisant les expressions, il est possible de comparer des chaines à des modèles identiques à celles permettant le remplacement des noms de fichiers.
Caractères spéciaux pour modèles de chaines de caractères | Signification |
---|---|
Caractères spéciaux valables dans tous les shells : | |
* | 0 à n caractères |
? | 1 caractère quelconque |
[abc] | 1 caractère parmis ceux inscrits entre les crochets |
[!abc] | 1 caractère ne faisant pas partie de ceux inscrits entre les crochets |
Caractères spéciaux non valides en Bourne Shell. En bash, il faut activer l'option extglob (shopt -s extglob) |
|
?(expression) | de 0 à 1 fois l'expression |
*(expression) | de 0 à n fois l'expression |
+(expression) | de 1 à n fois l'expression |
@(expression) | 1 fois l'expression |
!(expression) | 0 fois l'expression |
?(expression1 | expression2 | ...) *(expression1 | expression2 | ...) +(expression1 | expression2 | ...) @(expression1 | expression2 |...) !(expression1 | expression2 | ...) |
alternatives |
Exemple :
Le script suivant test si le numéro de téléphone saisi correspond bien au format +33240346523 ou 0240346523
$ nl test_telephone.sh
1 #! /bin/bash
2 echo -e "Saisir un numéro de téléphone : \c"
3 read telephone
4 # Test si le téléphone saisi est de la forme
5 # +33240346523 ou 0240346523
6 if [[ $telephone = @(+33)@([1-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9]) ||
7 $telephone = @(0)@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9]) ]]
8 then
9 echo "Le numéro est correct"
10 exit 0
11 else
12 echo "Le numéro est incorrect"
13 exit 1
14 fi
$ ./test_telephone.sh
Saisir un numéro de téléphone : 0240020202
Le numéro est correct
$ ./test_telephone.sh
Saisir un numéro de téléphone : +33256985478
Le numéro est correct
$ ./test_telephone.sh
Saisir un numéro de téléphone : 2356958457
Le numéro est incorrect
$ ./test_telephone.sh
Saisir un numéro de téléphone : +33025146987
Le numéro est incorrect
$ ./test_telephone.sh
Saisir un numéro de téléphone : g52365
Le numéro est incorrect
$
Rappels :
Commande test ( [ ] ) | Commande [[ ]] | Signification |
---|---|---|
\(.....\) | (.....) | Regroupement d'expressions |
! | ! | Négation |
-a | && | ET logique |
-o | || | OU logique |
Exemple avec la commande test :
if [ -w $fic1 -a \( -e $rep1 -o -e $rep2 \) ]
then
.....
Exemple avec la commande [[ ]] :
if [[ -w $fic1 && ( -e $rep1 || -e $rep2 ) ]]
then
.....
Ces opérateurs permettent d'exécuter ou non une commande en fonction du code de retour d'une autre commande.
Opérateur | Signification |
---|---|
&& | ET logique |
|| | OU logique |
Syntaxe :
commande1 && commande2
La seconde commande (commande2) est exécutée uniquement si le code de retour de la commande (commande1) est égale à 0 (vrai).
Le code de retour global est égal à 0 (vrai) si le code de retour de chaque commande est égal à 0 (vrai).
Exemple :
Le répertoire temp/temp2 n'existe pas donc la commande cd n'est pas exécutée
$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ pwd
/home/toto
$ [[ -d temp/temp2 ]] && cd temp/temp2
$ echo $? # code de retour de la commande [[ ]]
1
$ pwd
/home/toto
$
Le répertoire temp/temp2 existe donc la commande cd est exécutée
$ mkdir temp/temp2
$ pwd
/home/toto
$ [[ -d temp/temp2 ]] && cd temp/temp2
$ echo $? # code de retour de la commande globale
0
$ pwd
/home/toto/temp/temp2
$
Le répertoire temp/temp2 existe mais le répertoire temp/temp3 n'existe pas donc la commande cd retourne un code erreur égal à 1
$ [[ -d temp/temp2 ]] && cd temp/temp3
-bash: cd: temp/temp3: Aucun fichier ou dossier de ce type
$ echo $? # code de retour de la commande cd
1
$
Ces actions peuvent également être exécutées avec la structure de commande if
$ pwd
/home/toto
$ ls -d temp/temp2
temp/temp2
$ if [[ -d temp/temp2 ]]
> then
> cd temp/temp2
> fi
$ echo $?
0
$ pwd
/home/toto/temp/temp2
$
Syntaxe :
commande1 || commande2
La seconde commande (commande2) est exécutée uniquement si le code de retour de la commande (commande1) est égale à 1 (faux).
Le code de retour global est égal à 0 (vrai) si au moins une des commandes retourne un code égal à 0 (vrai).
Exemple :
Le répertoire temp/temp2 n'existe pas donc la commande echo est exécutée
$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ [[ -d temp/temp2 ]] || echo "Le répertoire n'existe pas"
Le répertoire n'existe pas
$ echo $?
0
$
Le répertoire temp/temp2 existe donc la commande echo n'est pas exécutée
$ mkdir temp/temp2
$ ls -d temp/temp2
temp/temp2
$ [[ -d temp/temp2 ]] || echo "Le répertoire n'existe pas"
$ echo $?
0
$
Ces actions peuvent également être exécutées avec la structure de commande if
$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ if [[ ! -d temp/temp2 ]]
> then
> echo "Le répertoire n'existe pas"
> fi
Le répertoire n'existe pas
$
Ne pas confondre les opérateurs du shell && et || qui effectuent une opération logique entre deux commandes et les opérateurs && et || de la commande [[ ]] qui sont internes à celle-ci
Syntaxe :
expr $nbr1 opérateur $nbr2
expr $chaine : expression régulière
Opérateurs :
Attention, certains opérateurs ayant une signification particulière pour le shell, ceux ci doivent être protégés par un antislash.
Opérateurs | Signification |
---|---|
Opérateurs arithmétiques | |
$nb1 + $nb2 | Addition |
$nb1 - $nb2 | Soustraction |
$nb1 \* $nb2 | Multiplication |
$nb1 / $nb2 | Division |
$nb1 % $nb2 | Modulo |
Opérateurs de comparaison | |
$nb1 \> $nb2 | VRAI si $nb1 est strictement supérieur à $nb2 |
$nb1 \>= $nb2 | VRAI si $nb1 est supérieur ou égal à $nb2 |
$nb1 \< $nb2 | VRAI si $nb1 est strictement inférieur à $nb2 |
$nb1 \<= $nb2 | VRAI si $nb1 est inférieur ou égal à $nb2 |
$nb1 = $nb2 | VRAI si $nb1 est égal à $nb2 |
$nb1 != $nb2 | VRAI si $nb1 est diférent de $nb2 |
Opérateurs logiques | |
$chaine1 \& $chaine2 | VRAI si les 2 chaines sont vraies |
$chaine1 \| $chaine2 | VRAI si l'une des 2 chaines est vraie |
Opérateurs divers | |
-$nb1 | Opposé de $nb1 |
\( expression \) | Regroupement |
$chaine : expression_reguliere | Compare la chaine avec l'expression régulière |
Les arguments de la commande expr doivent toujours être séparés par au moins un espace ou une tabulation.
Exemple :
$ nb=3
$ expr $nb + 5
8
$ expr $nb \* 6
18
$ expr $nb / 2
1
$ nb=10
$ expr $nb % 3
1
$ expr $nb - -5
15
$
Récupérer le résultat dans une variable :
$ nb2=`expr $nb - 2`
$ echo $nb2
8
$
Priorité d'exécution des opérateurs :
$ nb=5
$ nb2=`expr $nb \* 3 + 4`
$ echo $nb2
19
$ nb2=`expr $nb \* \( 3 + 4 \)`
$ echo $nb2
35
$
Résultat d'une comparaison VS valeur du code de retour :
$ nb=2
$ expr $nb \>= 1
1 # Résultat de la comparaison
$ echo $?
0 # Résultat du code de retour
$ expr $nb \>= 3
0 # Résultat de la comparaison
$ echo $?
1 # Résultat du code de retour
$
Différents types de comparaison :
$ nl comparaison.sh
1 #!/bin/bash
2 # Test sur le nombre d'arguments
3 if [[ $# -ne 2 ]]
4 then
5 echo "Mauvais nombre d'arguments"
6 echo "Utilisation : $0 expr1 expr2"
7 exit 1
8 fi
9 # On compare expr1 et expr2 avec la commande EXPR et redirection du résultat dans /dev/null
10 if expr $1 \> $2 > /dev/null
11 then
12 echo "Comparaison EXPR : $1 est supérieur à $2"
13 else
14 echo "Comparaison EXPR : $1 est inférieur à $2"
15 fi
16 # On compare expr1 et expr2 avec la commande [[ ]] (lexicographiquement)
17 if [[ $1 > $2 ]]
18 then
19 echo "Comparaison lexico.. [[ ]] : $1 est supérieur à $2"
20 else
21 echo "Comparaison lexico.. [[ ]] : $1 est inférieur à $2"
22 fi
23 # On compare expr1 et expr2 avec la commande [[ ]]
24 if [[ $1 -gt $2 ]]
25 then
26 echo "Comparaison [[ ]] : $1 est supérieur à $2"
27 else
28 echo "Comparaison [[ ]] : $1 est inférieur à $2"
29 fi
30 exit 0
$ ./comparaison.sh 9 5
Comparaison EXPR : 9 est supérieur à 5
Comparaison lexico.. [[ ]] : 9 est supérieur à 5
Comparaison [[ ]] : 9 est supérieur à 5
$ ./comparaison.sh 50 9
Comparaison EXPR : 50 est supérieur à 9
Comparaison lexico.. [[ ]] : 50 est inférieur à 9
Comparaison [[ ]] : 50 est supérieur à 9
$ ./comparaison.sh a b
Comparaison EXPR : a est inférieur à b
Comparaison lexico.. [[ ]] : a est inférieur à b
Comparaison [[ ]] : a est inférieur à b
$ ./comparaison.sh t r
Comparaison EXPR : t est supérieur à r
Comparaison lexico.. [[ ]] : t est supérieur à r
Comparaison [[ ]] : t est inférieur à r
$
Le résultat de la commande EXPR est toujours exact que ce soit des chiffres ou des caractères.
Syntaxe :
((expression_arithmétique))
Utilisation :
La commande (( )) dispose de nombreux avantages par rapport à la commande expr
Une grande partie des opérateurs proviennent du langage C
Opérateurs | Signification |
---|---|
Opérateurs arithmétiques | |
nbr1 + nbr2 | Addition |
nbr1 - nbr2 | Soustraction |
nbr1 * nbr2 | Multiplication |
nbr1 / nbr2 | Division |
nbr1 % nbr2 | Modulo |
Opérateurs travaillant sur les bits | |
~nbr1 | Complément à 1 |
nbr1 >> nbr2 | Décalage sur nbr1 de nbr2 bits à droite |
nbr1 << nbr2 | Décalage sur nbr1 de nbr2 bits à gauche |
nbr1 & nbr2 | ET bit à bit |
nbr1 | nbr2 | OU bit à bit |
nbr1 ^ nbr2 | OU exclusif bit à bit |
Opérateurs de comparaison | |
nbr1 > nbr2 | VRAI si nbr1 est strictement supérieur à nbr2 |
nbr1 >= nbr2 | VRAI si nbr1 est supérieur ou égal à nbr2 |
nbr1 < nbr2 | VRAI si nbr1 est strictement inférieur à nbr2 |
nbr1 <= nbr2 | VRAI si nbr1 est inférieur ou égal à nbr2 |
nbr1 == nbr2 | VRAI si nbr1 est égal à nbr2 |
nbr1 != nbr2 | VRAI si nbr1 est différent de nbr2 |
Opérateurs logiques | |
!nbr1 | Inverse la valeur de vérité de nbr1 |
&& | ET |
|| | OU |
Opérateurs divers | |
-nbr1 | Opposé de nbr1 |
nbr1 = expression | Assignement |
(expression) | Regroupement |
nbr1 binop= nbr2 | binop représente l'un des opérateurs suivants : +, -, /, *, %, >>, <<, &, |, ^. Equivalent à nbr1 = nbr1 binop nbr2 |
Exemples :
Ajouter 10 à nbr1 (2 méthodes différentes)
$ nbr1=10
$ ((nbr1=nbr1+10))
$ echo $nbr1
20
$ nbr1=10
$ ((nbr1+=10))
$ echo $nbr1
20
$
Test si nbr1 est supérieur à nbr2 et inversement
$ nbr1=5
$ nbr2=6
$ ((nbr1>nbr2))
$ echo $?
1 # Code retour 1 (faux) car nbr1 n'est pas supérieur à nbr2
$ ((nbr1<nbr2))
$ echo $?
0 # Code retour 0 (vrai) car nbr1 est inférieur à nbr2
$
Le script suivant compare les 2 arguments passés en paramètre
$ nl comparaison2.sh
1 #!/bin/bash
2 # Test sur le nombre d'arguments
3 if (($#!=2))
4 then
5 echo "Mauvais nombre d'arguments"
6 echo "Utilisation : $0 nbr1 nbr2"
7 exit 1
8 fi
9 # On compare nbr1 et nbr2 avec la commande (( ))
10 if (($1<$2))
11 then
12 echo "$1 est inférieur à $2"
13 else
14 if (($1>$2))
15 then
16 echo "$1 est supérieur à $2"
17 else
18 if (($1==$2))
19 then
20 echo "$1 est égal à $2"
21 else
22 echo "Comparaison impossible"
23 fi
24 fi
25 fi
26 exit 0
$ ./comparaison2.sh 2 8
2 est inférieur à 8
$ ./comparaison2.sh 8 2
8 est supérieur à 2
$ ./comparaison2.sh 8 8
8 est égal à 8
$
Regroupements et tests logiques
$ nbr1=2
$ nbr2=5
$ if (( (nbr1>0) && (nbr2>nbr1) ))
> then
> echo "nbr1 est supérieur à 0 et inférieur à nbr2"
> else
> echo "nbr1 est égal à 0 ou supérieur à nbr2"
> fi
nbr1 est supérieur à 0 et inférieur à nbr2
$
La commande let est équivalente à ((expression))
Syntaxe :
let "expression"
Exemple :
Multiplier nbr1 par 3
$ nbr1=5
$ let "nbr1=nbr1*3"
$ echo $nbr1
15
$
Calculer le modulo de nbr1 par 2 et l'affecter à la variable nbr2
$ nbr1=5
$ let "nbr2=nbr1%2"
$ echo $nbr1
5
$ echo $nbr2
1
$
Il existe les caractères de substitution de commandes mais il existe également les caractères de substitution d'expressions arithmétiques.
Syntaxe :
commande argument1 $((expression_arithmetique)) ... argumentn
Exemple :
Rappel sur la substitution de commandes
$ echo "Nombre de users connectes : `who | wc -l`"
Nombre de users connectes : 1
ou
$ echo "Nombre de users connectes : $(who | wc -l)"
Nombre de users connectes : 1
Substitution d'expressions arithmétiques
$ cpt=1
$ ((cpt+=1)) # Le résultat de la commande n'est pas affichée
$ echo $cpt
2
$
$ cpt=1
$ echo "Nouveau compteur : `((cpt+=1))`"
Nouveau compteur : # Le résultat de la commande n'est pas affichée et le compteur n'est pas incrémenté
$ echo $cpt
1
$
$ cpt=1
$ echo "Nouveau compteur : $((cpt+=1))"
Nouveau compteur : 2 # Le résultat de la commande est affiché et le compteur est incrémenté
$ echo $cpt
2
$
Il ne faut pas confondre (( )) et $(( )).
(( )) est une commande interne au shell.
$(( )) sont des caractères spéciaux du shell à l'égal de `` ou $().
Le shell propose quelques options qui permettent de débugger des scripts shell.
L'option -x permet de débugger un script shell en affichant l'exécution des commandes après traitement des caractères spéciaux du shell.
Les différentes syntaxes permettant d'activer l'option -x :
Activer l'option
Désactiver l'option
Exemple :
Dans l'exemple suivant, la variable fichier n'a pas été préfixée par le symbole $.
$ nl liste.sh
1 #!/bin/bash
2 for fichier in `ls`
3 do
4 if [[ -f fichier ]]
5 then
6 echo "$fichier"
7 fi
8 done
$
L'exécution du script ne retourne aucun résultat malgré la présence de fichiers dans le dossier d'exécution du script.
$ ./liste.sh
$
L'exécution du script avec activation de l'option -x démontre effectivement que la variable fichier n'est pas remplacée par sa valeur.
$ bash -x liste.sh
++ ls
+ for fichier in '`ls`'
+ [[ -f fichier ]]
+ for fichier in '`ls`'
+ [[ -f fichier ]]
+ for fichier in '`ls`'
+ [[ -f fichier ]]
...
$
L'activation de l'option de débuggage peut également être lancée directement dans le script.
$ nl liste.sh
1 #!/bin/bash
2 set -x
3 for fichier in `ls`
4 do
5 if [[ -f fichier ]]
6 then
7 echo "$fichier"
8 fi
9 done
$
Après correction de l'erreur et exécution du script en mode débuggage.
$ nl liste.sh
1 #!/bin/bash
2 set -x
3 for fichier in `ls`
4 do
5 if [[ -f "$fichier" ]]
6 then
7 echo "$fichier"
8 fi
9 done
$ ./liste.sh
++ ls
+ for fichier in '`ls`'
+ [[ -f 1coucou ]]
+ echo 1coucou
1coucou
+ for fichier in '`ls`'
+ [[ -f 24902 ]]
+ for fichier in '`ls`'
+ [[ -f 25013 ]]
+ for fichier in '`ls`'
+ [[ -f 25031 ]]
+ for fichier in '`ls`'
+ [[ -f 25043 ]]
+ for fichier in '`ls`'
+ [[ -f comparaison2.sh ]]
+ echo comparaison2.sh
comparaison2.sh
...
$
Fonction | Bourne shell, ksh, bash | ksh, bash |
---|---|---|
Lecture des commandes sans exécution et détection des erreurs de syntaxe. | set -n set +n |
set -o noexec set +o noexec |
Affichage des commandes avant substitution des caractères spéciaux du shell | set -v set +v |
set -o verbose set +o verbose |
Exemple :
Dans l'exemple suivant, un guillemet a été volontairement omis.
$ nl liste.sh
1 #!/bin/bash
2 set -n
3 for fichier in `ls`
4 do
5 if [[ -f "$fichier ]]
6 then
7 echo "$fichier"
8 fi
9 done
$ ./liste.sh
./liste.sh: line 7: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « " » correspondant
./liste.sh: line 10: argument inattendu pour l'opérateur conditionnel à un argument
./liste.sh: line 10: Erreur de syntaxe : fin de fichier prématurée
$
$ nl liste.sh
1 #!/bin/bash
2 set -v
3 for fichier in `ls`
4 do
5 if [[ -f "$fichier ]]
6 then
7 echo "$fichier"
8 fi
9 done
$ ./liste.sh
for fichier in `ls`
do
if [[ -f "$fichier ]]
then
echo "$fichier"
fi
done
./liste.sh: line 7: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « " » correspondant
./liste.sh: line 10: argument inattendu pour l'opérateur conditionnel à un argument
./liste.sh: line 10: Erreur de syntaxe : fin de fichier prématurée
$
La structure de controle if permet de réaliser des tests.
La commande située à droite du if est exécutée.
Si le code retour de la commande ($?) est égal à 0 (vrai), les commandes situées dans le bloc then sont exécutées.
Si le code de retour est supérieur à 0 (faux), ce sont les commandes situées dans le bloc else (optionnel) qui sont exécutées.
Dans le cas où le bloc else n'est pas spécifié, le shell continue à la première commande située sous le fi.
Les différentes syntaxes :
if, then, else, fi
if commande1
then
commande2
commande3
...
else
commande4
...
fi
if, then, fi
if commande1
then
commande2
commande3
...
fi
if, then, elif, else, fi
if commande1
then
commande2
commande3
...
elif commande4
then
commande5
...
else
commande6
...
fi
Le mot clé fi permet de fermer la structure de controle if.
Le mot clé elif n'a pas de fermeture.
Autres syntaxes :
Le mot clé then peut être placé sur la même ligne que le if à condition de les séparer à l'aide d'un point virgule.
if commande1 ; then
commande2
commande3
...
fi
Plusieurs structures de controles if peuvent être imbriquées les unes dans les autres.
if commande1
then
commande2
...
else
if commande3
then
commande4
...
else
commande5
...
fi
fi
Exemple :
Le script suivant vérifie si un argument est passé en paramètre et dans le cas contraire demande une saisie clavier à l'utilisateur.
Le script vérifie ensuite que le user saisi existe bien dans le fichier /etc/passwd.
$ nl user_passwd.sh
1 #!/bin/bash
2 if [[ $# -ne 1 ]]
3 then
4 echo -e "Saisir le nom d'un user : \c"
5 read user
6 else
7 user=$1
8 fi
9 if grep -q "^$user:" /etc/passwd
10 then
11 echo "Le user $user existe"
12 echo "Son UID est le : $(grep "^$user:" /etc/passwd | cut -d":" -f3)"
13 echo "Son GID est le : $(grep "^$user:" /etc/passwd | cut -d":" -f4)"
14 else
15 echo "Le user $user n'existe pas !!!"
16 fi
17 exit 0
$ ./user_passwd.sh root
Le user root existe
Son UID est le : 0
Son GID est le : 0
$ ./user_passwd.sh
Saisir le nom d'un user : www-data
Le user www-data existe
Son UID est le : 33
Son GID est le : 33
$
La structure de controle case permet elle aussi d'effectuer des tests.
Elle permet d'orienter la suite du programme en fonction d'un choix de différentes valeurs.
Quand il y a un nombre important de choix, la commande case est plus appropriée que la commande if.
Syntaxe :
case $variable in
modele1) commande1
...
;;
modele2) commande2
...
;;
modele3 | modele4 | modele5 ) commande3
...
;;
esac
Le shell compare la valeur de la variable aux différents modèle renseignés.
Lorsque la valeur correspond au modèle, les commandes faisant partie du bloc sont exécutées.
Les caractères ;; permettent de fermer le bloc et de mettre fin au case.
Le shell continue à la première commande située sous esac.
Il ne faut surtout pas oublier les caractères ;; car cela engendrera une erreur.
Rappel des caractères spéciaux :
Caractères spéciaux pour modèles de chaines de caractères | Signification |
---|---|
Caractères spéciaux valables dans tous les shells : | |
* | 0 à n caractères |
? | 1 caractère quelconque |
[abc] | 1 caractère parmis ceux inscrits entre les crochets |
[!abc] | 1 caractère ne faisant pas partie de ceux inscrits entre les crochets |
Caractères spéciaux non valides en Bourne Shell. En bash, il faut activer l'option extglob (shopt -s extglob) |
|
?(expression) | de 0 à 1 fois l'expression |
*(expression) | de 0 à n fois l'expression |
+(expression) | de 1 à n fois l'expression |
@(expression) | 1 fois l'expression |
!(expression) | 0 fois l'expression |
?(expression1 | expression2 | ...) *(expression1 | expression2 | ...) +(expression1 | expression2 | ...) @(expression1 | expression2 |...) !(expression1 | expression2 | ...) |
alternatives |
Exemple :
Le script suivant permet de créer, modifier, visualiser et supprimer un fichier dans le répertoire d'exécution du script.
Il prend en argument un nom de fichier et affiche un menu.
Utilisation de case avec imbrication de if.
$ nl file.sh
1 #!/bin/bash
2 #set -x
3 # Si le nombre d'arguments est different de 1 on quitte avec code 1
4 if [[ $# -ne 1 ]]
5 then
6 echo "Nombre d'arguments incorrect"
7 echo "Usage : $0 file"
8 exit 1
9 fi
10 # On affiche le menu
11 echo -e "1(Creer) "
12 echo -e "2(Editer) "
13 echo -e "3(Afficher) "
14 echo -e "4(Supprimer)"
15 echo -e "Votre choix : \c"
16 # On recupere la valeur saisi
17 read choix
18 # Si la valeur saisi est differente de 1, 2, 3 ou 4 on quitte avec code 1
19 if [[ "$choix" != [1-4] ]]
20 then
21 echo "Choix incorrect"
22 exit 1
23 fi
24 # En fonction de la valeur saisi on execute les differentes actions
25 case "$choix" in
26 # Si choix = 1 --> creation
27 1) if [[ -e "$1" ]]
28 then
29 if [[ -f "$1" ]]
30 then
31 echo "Fichier $1 deja existant"
32 elif [[ -d "$1" ]]
33 then
34 echo "$1 est un repertoire"
35 fi
36 exit 1
37 else
38 touch "$1"
39 nano "$1"
40 fi
41 ;;
42 # Si choix = 2 --> edition
43 2) if [[ -f "$1" ]]
44 then
45 nano "$1"
46 else
47 if [[ -d "$1" ]]
48 then
49 echo "$1 est un repertoire et ne peut etre edite"
50 else
51 echo "Fichier $1 inexistant"
52 fi
53 exit 1
54 fi
55 ;;
56 # Si choix = 3 --> affichage
57 3) if [[ -f "$1" ]]
58 then
59 more "$1"
60 else
61 if [[ -d "$1" ]]
62 then
63 echo "$1 est un repertoire et ne peut etre visualise"
64 else
65 echo "Fichier $1 inexistant"
66 fi
67 exit 1
68 fi
69 ;;
70 # Si choix = 4 --> suppression
71 4) if [[ -f "$1" ]]
72 then
73 rm "$1"
74 else
75 if [[ -d "$1" ]]
76 then
77 echo "$1 est un repertoire et ne peut etre supprime"
78 else
79 echo "Fichier $1 inexistant"
80 fi
81 exit 1
82 fi
83 ;;
84 # Fin du case
85 esac
86 # Tout c'est bien deroule on quitte avec le code 0
87 exit 0
$ ./file.sh test4
1(Creer)
2(Editer)
3(Afficher)
4(Supprimer)
Votre choix : 1
$
Syntaxe :
La boucle for permet de traiter une liste de valeurs indiquée à droite du mot clé in.
A chaque tour de boucle, la variable var est initialisée avec une des valeurs de la liste.
Elles sont traitées dans l'ordre de leur énumération.
Liste de valeurs citée directement
for var in valeur1 valeur2 valeur3 ... valeurn
do
commande
done
Liste de valeurs contenue dans une variable
for var in $variable
do
commande
done
Liste de valeurs générée par substitution de commande
for var in `commande`
do
commande
done
Liste de valeurs générée par substitution de caractères de génération de noms de fichiers
for var in *.ext
do
commande
done
Liste par défaut : Arguments de la ligne de commande
for var
do
commande
done
for var in $*
do
commande
done
Avec incrémentation d'une variable
for (( var=valeurMin; var<=valeurMax; var++ ))
do
commande
done
Exemple :
Un script compte à rebours
$ nl boucleFor01.sh
1 #!/bin/bash
2 for var in 10 9 8 7 6 5 4 3 2 1 0
3 do
4 echo "$var"
5 done
6 exit 0
$ ./boucleFor01.sh
10
9
8
7
6
5
...
$
Lister les fichiers d'un ou plusieurs dossiers
$ nl boucleFor02.sh
1 #!/bin/bash
2 if [[ $# -lt 1 ]]
3 then
4 echo "Nombre d'argument incorrect"
5 echo "Utilisation $0 dossier1 dossier2 dossiern"
6 exit 1
7 fi
8 for dossier in $*
9 do
10 if [[ -d $dossier ]]
11 then
12 echo "Liste des fichiers du dossier $dossier"
13 for fichier in `ls $dossier`
14 do
15 echo "$fichier"
16 done
17 fi
18 done
19 exit 0
$ ./boucleFor02.sh coucou 24902 25013 25031
Liste des fichiers du dossier coucou
test
Liste des fichiers du dossier 24902
fichier_0
fichier_1
fichier_2
fichier_3
Liste des fichiers du dossier 25013
fichier_0
fichier_1
fichier_2
Liste des fichiers du dossier 25031
fichier_0
fichier_1
fichier_2
fichier_3
$
En utilisant une variable incrémentée :
$ for (( i=0; i <= 10; i++ )); do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
$
Idem avec la syntaxe suivante :
$ for i in {0..10}; do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
$
Syntaxe :
while commande1
do
commande2
...
done
La boucle while permet d'exécuter les commandes présentes entre le do et le done tant que la commande1 placée à droite du while retourne un code vrai.
Exemple :
Le script suivant demande de saisir 53 et continue tant que c'est faux
$ nl boucleWhile01.sh
1 #!/bin/bash
2 nbr=0
3 while ((nbr!=53))
4 do
5 echo -e "Saisir 53 : \c"
6 read nbr
7
8 done
9 exit 0
$ ./boucleWhile01.sh
Saisir 53 : rt
Saisir 53 : 54
Saisir 53 : R4
Saisir 53 : 53
$
Le script suivant affiche le compteur tant qu'il est inférieur à 10
$ nl boucleWhile02.sh
1 #!/bin/bash
2 cpt=0
3 while ((cpt<10))
4 do
5 echo "Le compteur vaut : $cpt"
6 ((cpt+=1))
7 done
8 exit 0
$ ./boucleWhile02.sh
Le compteur vaut : 0
Le compteur vaut : 1
Le compteur vaut : 2
Le compteur vaut : 3
Le compteur vaut : 4
Le compteur vaut : 5
Le compteur vaut : 6
Le compteur vaut : 7
Le compteur vaut : 8
Le compteur vaut : 9
$
Le script suivant effectue une somme des nombres saisis
$ nl boucleWhile03.sh
1 #!/bin/bash
2 somme=0
3 echo "Saisir un nombre, ^d pour afficher la somme"
4 while read nombre
5 do
6 if [[ $nombre != +([0-9]) ]]
7 then
8 echo "$nombre n'est pas un nombre"
9 continue
10 fi
11 ((somme+=nombre))
12 done
13 echo "La somme est de : $somme"
14 exit 0
$ ./boucleWhile03.sh
Saisir un nombre, ^d pour afficher la somme
56
32
89
9.6
9.6 n'est pas un nombre
g8
g8 n'est pas un nombre
54
La somme est de : 231
$
Le mot clé continue permet de remonter aussitôt à la boucle while sans exécuter la commande suivante
Attention aux boucles infinies
Ce script provoqe une boucle infinie car il manque l'incrémentation du compteur
$ nl boucleWhile04.sh
1 #!/bin/bash
2 cpt=0
3 while ((cpt<10))
4 do
5 echo "Le compteur vaut : $cpt"
6 done
7 exit 0
$
Le shell propose également la commande interne : qui renvoie toujours vrai et permet donc de faire une boucle infinie avec un while.
$ nl boucleWhile05.sh
1 #!/bin/bash
2 while :
3 do
4 echo "Boucle infinie"
5 done
6 exit 0
$
En bash et ksh, la commande true propose exactement la même chose.
$ nl boucleWhile05.sh
1 #!/bin/bash
2 while true
3 do
4 echo "Boucle infinie"
5 done
6 exit 0
$
Syntaxe :
until commande1
do
commande2
...
done
A l'inverse de while, la commande until exécute les commandes situées entre le do et le done tant que la commande située à droite du until retourne un code faux.
Exemple :
Le script suivant boucle tant que le nombre saisi n'est pas égal à 53
$ nl boucleUntil01.sh
1 #!/bin/bash
2 nbr=0
3 until ((nbr==53))
4 do
5 echo -e "Saisir 53 : \c"
6 read nbr
7 done
8 exit 0
$ ./boucleUntil01.sh
Saisir 53 : 45
Saisir 53 : rt
Saisir 53 : fd
Saisir 53 : 53
$
Le script suivant permet, en tâche de fond, de surveiller un répertoire donné et d'informer l'utilisateur de l'arrivée d'un fichier attendu dans ce répertoire.
Pour plus de sécurité sur l'intégrité du fichier attendu, un fichier témoin devra être créé à la suite du fichier attendu puisque le principal contrôle se fera sur l'existence de ce fichier.
$ nl boucleUntil02.sh
1 #!/bin/bash
2 # Il doit y avoir au minimum 2 paramètres en arguments et un maximum de 3
3 if [[ $# -lt 2 || $# -gt 3 ]]
4 then
5 echo "Utilisation : $0 repertoire fichier [ temoin ]"
6 exit 1
7 fi
8 # Le premier argument doit être un répertoire
9 if [[ ! -d $1 ]]
10 then
11 echo "$1 n'est pas un répertoire"
12 exit 2
13 fi
14 # Nom du fichier témoin par défaut
15 ficTemoin=${3:-temoin}
16 # Exécution de la boucle en attendant l'arrivée du fichier témoin avec une pause toutes les 3 secondes
17 until [[ -e $1/$ficTemoin ]]
18 do
19 sleep 3
20 done
21 # Vérification que le fichier attendu est bien présent
22 if [[ ! -e $1/$2 ]]
23 then
24 echo "Le fichier témoin existe mais le fichier attendu est absent"
25 exit 3
26 fi
27 # Sauvegarde du fichier attendu dans le HOME de l'utilisateur avec horodatage et suppression du fichier témoin
28 date=$(date '+%Y%m%d_%H%M')
29 newFichier=$2.$date
30 mv $1/$2 $HOME/$newFichier
31 rm $1/$ficTemoin
32 # Envoi d'un mail à l'utilisateur
33 mail $LOGNAME <<FIN
34 Le fichier $HOME/$newFichier est bien arrivé.
35 FIN
36 echo "$0 : Vous avez reçu un message !!! "
37 exit 0
$ ./boucleUntil02.sh /tmp monFichier & # Lancement du script en arrière plan grâce à la commande &
[1] 2298
$ touch /tmp/monFichier # Création du fichier attendu
$ touch /tmp/temoin # Création du fichier témoin
$ ./boucleUntil02.sh : Vous avez reçu un message !!! # Message généré par la ligne 36
[1]+ Done ./boucleUntil02.sh /tmp monFichier
$
il ne reste plus qu'à consulter sa boite mail pour lire le message envoyé par le script.
Les commandes break et continue peuvent s'utiliser à l'intérieur des boucles for, while, until et select.
La commande break permet de sortir d'une boucle.
La commande continue permet de remonter à la condition d'une boucle.
Syntaxe :
Quitter la boucle de premier niveau
break
Quitter la boucle de niveau n
break n
Remonter à la condition de la boucle de premier niveau
continue
Remonter à la condition de la boucle de niveau n
continue n
Exemple :
$ nl boucleWhile06.sh
1 #!/bin/bash
2 somme=0
3 while true
4 do
5 echo "Saisir un nombre, ^d pour afficher la somme"
6 if read nombre
7 then
8 if [[ $nombre != +([0-9]) ]]
9 then
10 echo "$nombre n'est pas un nombre"
11 continue
12 fi
13 ((somme+=nombre))
14 else
15 break
16 fi
17 done
18 echo "La somme est de : $somme"
19 exit 0
$ ./boucleWhile06.sh
Saisir un nombre, ^d pour afficher la somme
23
Saisir un nombre, ^d pour afficher la somme
56
Saisir un nombre, ^d pour afficher la somme
54
Saisir un nombre, ^d pour afficher la somme
89
Saisir un nombre, ^d pour afficher la somme
La somme est de : 222
$