Séance 5 : Automatisation et scripts¶
Variables et environnement¶
Variable¶
Le shell permet de définir et réutiliser des variables :
$ message="hello world"
$ echo $message
hello world
Avertissement
Il ne faut pas d'espace autour du signe =
.
Note
Les guillemets sont nécessaires si la valeur de la variable contient des espaces
Le méta-caractère $¶
Le méta-caractère dollar ($
), suivi d'un nom de variable,
est remplacé par la valeur de la variable en question.
Cette substitution a lieu avant d'analyser les autres caractères. Ainsi, si la valeur de la variable contient des espaces, cela pourra donner lieu à plusieurs arguments dans la ligne de commande :
$ cp $message # copie le fichier 'hello' en 'world'
Important
Le méta-caractère dollar n'est pas inhibié par les guillemets doubles ("
).
En revanche, il l'est par les guillemets simples ('
).
$ echo "Simon says $message"
Simon says hello world
$ echo 'Simon says $message'
Simon says $message
Note
Si le nom utilisé après $ ne correspond à aucune variable définie, il est substitué par une chaîne vide (sans causer d'erreur).
unset <variable>¶
La commande unset
suivi d'un nom de variable (sans le méta-caractère $
)
supprime la variable :
$ unset message
$ echo $message
$
Variable d'environnement¶
Pour définir une variable d'environnement, il faut précéder son affectation du mot-clé export :
export MANPAGER=more
Note
Par convention, les variables d'environnement sont en majuscules, alors que les variables de shell sont en minuscules.
Différences¶
Les variables de shell
sont gérées en interne par le shell ;
ne sont pas accessible aux autres processus.
Les variables d'environnement
sont gérées par le SE ;
sont héritées par les processus lancés depuis le shell ;
sont utilisées par certains programmes pour modifier leur comportement.
Exemple de configuration par l'environnement :
$ man ls # suivi de CTRL+Z
$ ps -H
PID TTY TIME CMD
28608 pts/4 00:00:00 bash
29162 pts/4 00:00:00 man
29172 pts/4 00:00:00 pager
29188 pts/4 00:00:00 ps
$ kill %1
$ export MANPAGER=more
$ man ls # suivi de CTRL+Z
$ ps -H
PID TTY TIME CMD
28608 pts/4 00:00:00 bash
29425 pts/4 00:00:00 man
29435 pts/4 00:00:00 more
29442 pts/4 00:00:00 ps
Quelques variables d'environnement¶
PATH
liste des répertoires, séparés par
:
, dans lesquels le shell cherche les programmes à lancerPS1
prompt affiché par le shell
Note
La variable d'environnement PATH
ne contient pas le répertoire courant .
,
et ce pour des raisons de sécurité.
Cela évite de lancer par erreur un programme local nommé ls
ou cp
,
et qui pourrait avoir des effets inattendus (ou malicieux).
env¶
Affiche sur la sortie standard toutes les variables d'environnement actuellement définie.
Scripts¶
Un ensemble de commandes shell peuvent être stockées dans un fichier, pour être réutilisées plus tard.
Méthode 1 : la commande source¶
$ source commandes.sh
(sortie des instructions contenues dans commandes.sh)
$ . commandes.sh # '.' est équivalent à 'source'
(sortie des instructions contenues dans commandes.sh)
Execute dans le shell en cours les commandes contenues dans le fichier commandes.sh
.
Indication
Ce n'est pas la méthode la plus fréquente, notamment parce que les variables utilisées dans le fichier pourraient « polluer » le shell courant.
On l'utilise justement lorsque l'objectif est de modifier le shell courant ou les variables d'environnement.
Méthodes 2 : script exécutable¶
La première ligne du fichier doit être
#!/usr/bin/bash
.L'utilisateur doit avoir les droits en exécution sur le fichier.
Le fichier peut alors être lancé comme n'importe quel programme :
$ ./commandes.sh (sortie des instructions contenues dans commandes.sh)
Le shell exécutant les commande du script est un processus séparé du shell courant.
Note
La première ligne est appellée hash-bang (d'après les noms des deux premiers caractères) ou shebang.
Cette ligne indique à Linux quel interpréteur utiliser pour exécuter le fichier ; on peut y préciser n'importe quel exécutable, par exemple
/usr/bin/python
,/usr/bin/ruby
...
Commentaires¶
Le caractère dièse (#
, plus exactement hash)
introduit un commentaire, ignoré par le shell.
Le commentaire s'étend jusqu'à la fin de la ligne.
$ echo texte affiché # commentaire non affiché
texte affiché
Note
Puisque le shebang commence par #, il est ignoré par le shell lui même, seul le SE l'interprète.
Arguments¶
Lorsqu'on appelle un script (quelle que soit la méthode), on peut lui passer des arguments en ligne de commande.
Ces arguments sont accessibles dans des variables spéciales :
$1
,$2
,$3
... contiennent les différents arguments,$@
contient la séquence entière d'arguments.
Flux standards¶
Rappel : tout processus lancé par un shell (y compris via un script) hérite de ses flux (entrée, sortie, erreur) standards.
Ainsi, les redirections appliquées à un script s'appliquent aux commandes lancées par ce script.
Contenu du fichier sort_column.sh
:
#!/usr/bin/bash
# extrait et trie une colonne
# des données passées sur l'entrée standard
cut -d: -f$1 | sort
Exemple d'utilisation:
$ ./sort_column.sh 1 </etc/passwd
read [options] <varname>...¶
Lit une ligne de texte sur l'entrée standard, et affecte le texte lu dans une ou plusieurs variable(s).
Par défaut, la lecture s'interromp au premier saut de ligne.
Retourne un succès si du texte est lu, un échec si la fin de fichier est rencontrée.
-
varname
Le texte lu est découpée au niveau des espaces ; la première variable reçoit le premier mot, la deuxième variable, le deuxième mot... et la dernière variable reçoit le reste du texte lu.
-
-n
[nchars]
¶ Interromp la lecture au bout de
nchars
caractères.
Note
Avec un seul nom de variable, l'intégralité du texte lu est affecté à cette variable.
Structures de contrôle¶
Le shell fournit des structures de contrôles similaires à celles des langages de programmation.
Structure if¶
if condition
then
commande 1
commande 2
# ...
else
commande 3
# ...
fi
Note
condition
est une ligne de commande ; la valeur de retour du processus détermine si c'est lethen
(succès) ou leelse
(échec) qui sera exécuté.La clause
else
est facultative.Le mot-clé
fi
est formé comme l'inverse du mot-cléfi
.Comme partout ailleurs, les sauts de ligne peuvent être remplacés par des points-virgules (
;
).L'indentation n'est strictement requise, mais conseillée pour la lisibilité du code.
Exemple :
if grep -q charlie </etc/passwd
then
echo "je l'ai trouvé"
fi
Structure while¶
while condition
do
commande 1
commande 2
# ...
done
Note
Comme pour le
if
, lecondition
est une ligne de commande.
Exemple :
while read line
do
echo "Jacques a dit $line"
done
echo "Jacques a fini"
Lecture depuis un fichier avec while¶
La commande cat
ré-écrit sur sa sortie standard le contenu de tous les fichiers qui lui sont passés en argument (son nom est une abbréviation de concatenate).
Boucle sur toutes les lignes d'un fichier :
cat my_file.txt | while read line
do
# do something with $line
done
Structure for¶
for varname in liste de valeurs
do
commande 1
commande 2
done
Note
La variable prend successivement chaque valeur de la liste.
Pour chaque valeur, la liste de commande est exécutée.
Exemple :
for i in A B C "D E F"
do
echo $i
done
Comme pour le reste,
les variables sont substituées avant l'analyse de la ligne,
donc une variable contenant plusieurs mots (séparés par des espaces)
peut être utilisée après le mot-clé in
.
for i in $valeurs
do
echo $i
done
On peut également utiliser la substitution par guillemets inversés.
for grp in `id -Gn`
do
echo "Vous appartenez au groupe $grp"
done
Avertissement
Attention avec ce dernier exemple ; il ne fonctionne pas comme attendu si certains éléments contiennent des espaces.
Précaution avec la boucle for¶
N'écrivez pas :
for filename in `ls`
do
cp $filename $filename.bak
done
Mais écrivez :
ls | while read filename
do
cp "$filename" "$filename.bak"
done
Note
ls
écrit un nom de fichier par ligne ;
ainsi, chaque nom de fichier sera lu en une fois par read
,
même s'il contient des espaces.
Cette précaution vaut bien sûr pour d'autres données, dont certaines lignes peuvent contenir des espaces.
[ <testarg> ]¶
La commande [ évalue une expression booléenne, décrite par ses arguments, et retourne un code de statut correspondant.
Le dernier argument doit être ]
.
NB: les espaces séparant les opérateurs et les opérandes sont indispensable,
pour que le shell les transmette à la commande [
comme des arguments séparés.
-
STRING1 = STRING2
Vrai si les deux chaînes sont égales.
-
STRING != STRING2
Vrai si les deux chaînes sont différentes.
-
INTEGER1 -lt INTEGER2
Vrai si les deux opérandes sont des entiers, et que INTEGER1 est strictement inférieur à INTEGER2.
Existent également
-le
(≤),-gt
(>),-ge
(≥).
-
-f FILENAME
Vrai si FILENAME est le nom d'un fichier classique existant.
-
-d FILENAME
Vrai si FILENAME est le nom d'un répertoire existant.
-
EXPR1 -a EXPR2
Vrai si les deux expressions sont vraies.
-
EXPR2 -o EXPR2
Vrai si l'une au moins des deux expressions est vraie.
-
! EXPR
Vrai si EXPR est fausse.
Pour une documentation exhaustive, tapez man [
.
Exemple :
#!/usr/bin/bash
if [ -d "$1" -a -f "$2" ]
then
cp "$2" "$1"/"$2".bak
else
echo "Invalid arguments" >&2
exit 1
fi
Note
>&2
est une redirection particulière,
qui redirige la sortie standard vers l'erreur standard.
Elle permet ici d'utiliser echo
pour écrire sur l'erreur standard
(alors que son comportement normal est décrire sur la sortie standard).
Valeurs de retour¶
La valeur de retour d'un script est celle de la dernière commande exécutée par ce script.
La commande
exit <integer>
sort immédiatement du script, en retournant la valeur passée en argument.En l'absence d'argument,
exit
sort du script avec la valeur de retour de la dernière commande exécutée.
Valeurs de retour intermédiaire¶
L'échec d'une commande dans un script n'entraîne pas la fin du script.
Si on souhaite interrompre un script en cas d'erreur d'une de ses commandes intermédiaires, on peut la faire suivre de
|| exit
.
Épilogue¶
Le shell permet d'exécuter des tâches relativement complexes, en combinant plusieurs commandes spécialisées.
Les scripts shells permettent de stocker et réutiliser des séquences de commandes.
Mais le shell n'est pas adapté pour développer des applications complètes.
Lorsqu'un script devient trop complexe, il ne faut pas hésiter à le ré-écrire dans un vrai langage de programmation (quitte à garder certaines parties sous forme de script shell).
Travaux dirigés¶
Exercice 1¶
Écrivez un script qui attend jusqu'à quatre arguments sur la ligne de commande, et les affiche en ordre inverse.
Les arguments au delà du quatrième seront ignorés.
Exercice 2¶
Écrivez une variante du script précédent, qui attend exactement quatre arguments.
Il affichera un message d'erreur, et retournera un code d'échec, si l'utilisateur saisit le mauvais nombre d'arguments.
Exercice 3¶
Écrivez une variante du script précédent, ou les quatre mots seront attendus sur l'entrée standard (sur une seule ligne) plutôt que comme arguments.
Exercice 4¶
Écrivez un script qui crée 5 fichiers fic1.txt
à fic5.txt
,
contenant respectivement les valeurs 1
à 5
,
dans un répertoire passé en paramètre.
Si ce répertoire n'existe pas, ou si un des cinq fichiers existe déjà, un message d'erreur doit être affiché.
Exercice 5¶
Écrivez un script qui affiche, sur la même ligne, un compte à rebours de 5 secondes, puis affiche sur la ligne suivante le mot « décollage ».
Indication
la commande
sleep <number>
attend sans rien faire pendant le nombre de secondes passé en paramètres ;l'option
-n
de la commandeecho
lui indique de ne pas afficher de retour à la ligne.
Exercice 6¶
Écrivez un script qui affiche le nom de tous les fichiers du répertoire /usr/include
dont le nom se termine par .h
et ayant plus de 100 lignes.
Exercice 7¶
Dans cet exercice et le suivant, on réutilisera le fichier contacts.txt utilisé lors d'une précédente séance.
Écrivez une commande contacts_ville.sh
qui prend en paramètre le nom d'une ville,
et affiche uniquement les lignes du fichier contacts.txt
concernant un habitant de cette ville.
Avertissement
La ligne de commande ./contacts_ville.sh Lyon
devrait retourner uniquement Alice,
Bob et Jean Dupont. Jane Lyon (qui habite Paris) ne doit pas être affichée.
Variante : faites en sorte que la recherche de la ville soit insensible à la casse (majuscule-minuscule).
Exercice 8¶
Écrivez une commande info_contact.sh
qui prend deux paramètres :
le nom ou une partie du nom d'un contact,
l'une des deux chaînes
ville
outel
,
et qui affiche, pour tous les contacts dont le nom contient le premier argument, son nom suivi de sa ville ou de son numéro de téléphone (selon le deuxième argument).
Si le deuxième argument n'est pas une des deux chaînes attendues, le script doit afficher un message d'erreur et retourner un code d'échec.
Avertissement
La ligne de commande ./info_contacts.sh lyon tel
devrait afficher uniquement le numéro de téléphone de Jane Lyon, mais pas celui d'Alice, Bob ou Jean Dupont.
Variante : faites en sorte que le chemin du fichier contacts.txt
soit pris dans la variable d'environnement CONTACTS
. À défaut, utilisez ./contacts.txt
.