Copyright © 2000 Ben Okopnik
Copyright © 2000 Xavier Serpaggi
Article paru dans le n°52 de la Gazette Linux d'avril 2000.
Cet article est publié selon les termes de la Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.
Table des matières
Voici un truc. Si vous pensez que votre code servant à lancer une fonction shell ne fonctionne pas, ne lui passez jamais, je dit bien jamais l'argument « /etc/reboot » histoire de voir ce que ça fait. | ||
-- Elliot Evans |
Les scripts shell sont une combinaison fascinante d'art et de science qui, sous la forme d'outils très simple, vous donnent accés à l'incroyable flexibilité et à l'incroyable puissance de Linux. À l'époque où les PC n'en étaient qu'à leur début, j'étais considéré comme un expert des fichiers batch de DOS. Maintenant je réalise qu'ils n'étaient qu'une pâle imitation sans âme des scripts shell d'Unix. Je n'ai pas l'habitude de donner dans l'anti-Microsoft — je crois réellement qu'en leur temps ils ont fait des choses vraiment bien — mais leur langage BFL (Batch File Language) était une plaisanterie en comparaison. Et elle n'était même pas drôle.
Vu que les scripts sont partie intégrante du shell lui-même, une partie de cet article parlera des bizzareries du shell, de ses méthodes et de ses spécificités. Soyez patients. Cela fait partie intégrante du savoir nécessaire à l'écriture de bons scripts.
Linux, en général, n'est pas un système accueillant, chaleureux et destiné à des utilisateurs non avertis. Plutôt que de vous dire pas à pas ce que vous devez faire, il met à votre disposition une myriade de petits outils, qui peuvent être combinés d'une infinité de façons, pour aboutir à un résultat donné (Je trouve la devise de Perl TMTOWTDI — There's More Than One Way To Do It (Il existe plus d'une façon de le faire) — tout à fait à propos pour tous les Unix). Cette forme de puissance et de flexibilité, bien sûr, a un prix : une plus grande complexité et des compétences plus poussées pour l'utilisateur. De la même manière qu'il existe une énorme différence dans la manière de conduire disons, une bicyclette et un avion de combat supersonique, il existe une énorme différence entre suivre à l'aveuglette les dictats rigides d'une interface utilisateur standardisée et créer vos propres programmes (ou scripts shell) qui font exactement ce que vous voulez qu'ils fassent et de la manière que voulez qu'ils le fasse.
Écrire des scripts shell est une manière de programmer — mais c'est de la programmation facile à appréhender, avec peut (quand il y en a) de structure formelle. C'est un langage interprété proposant sa propre syntaxe — mais ce n'est rien d'autre que la syntaxe que vous utilisez quand vous lancez des programmes depuis la ligne de commande ; parfois je nomme ça du savoir recyclable. C'est en fait ce qui rend les scripts shell si utiles : quand vous les écrivez vous en apprenez toujours plus sur les spécificités de votre shell et sur les opérations de votre système — et c'est vraiment ce savoir qui, à court comme à long terme, paie.
Vu que ma préférence va largement à bash et qu'en plus il apparaît être le shell le plus utilisé, vous comprendrez pourquoi ces scripts sont écrits pour lui. Mais il n'y a aucun problème a utiliser un autre shell ; il suffit que vous ayez bash d'installé et tout ira très bien. Comme vous le verrez, les script font appel au shell dont ils ont besoin. Cela fait partie des attributions d'un script bien écrit.
Je vais faire l'hypothèse que vous êtes dans votre répertoire personnel. Cela
évitera d'avoir des fichier dispersés partout de telle manière qu'on ne
puisse plus les retrouver par la suite. Je vais également supposer que vous en
savez assez pour appuyer sur la touche Enter après chaque ligne que vous
avez tapé, et qu'une fois que vous aurez choisi un nom pour votre script, vous
verifirez qu'il n'existe pas d'exécutable portant le même dans votre path
(Truc : tapez which bkup pour vérifier si il y a un exécutable du nom de
bckup dans votre path
). C'est pour cette raison que vous ne devrez
jamais appeler vos scripts test
. C'est une des FAQ d'Unix :
« Pourquoi est-ce que mon script shell/programme ne fait rien ? » Il
existe un exécutable dans /bin
appelé test qui ne fait rien (rien
d'apparent en tout cas) quand on le lance...
Vous devez également connaître les principes de base des opérations sur les fichiers (copie, déplacement, etc.) ainsi qu'être familier avec les abréviations usuelles (« . » est le répertoire courant, « .. » le répertoire parent, « ˜ » votre répertoire personnel, etc.), cela va sans dire. Vous ne le saviez pas ? Et bien maintenant vous le savez ! Hé hé...
Quelque soit l'éditeur que vous utilisez, vi, emacs, mcedit (l'éditeur par défaut de Midnight Commander et un de mes outils préférés) ou autre, il conviendra ; prenez simplement garde de ne pas sauvegarder vos scripts sous un format propre à un traitement de texte.
Pour éviter les répétition je vais numéroter les lignes au fur et à mesure que nous avancerons dans la description du script. De toutes façon, je remettrai tout dans l'ordre à la fin.
Examinons ensemble les bases de la création d'un script. Ceux d'entre vous
qui trouveraient cela évident sont tout de même invités à lire ce qui suit ;
alors que nous progresserons, tout se compliquera et un petit raffraîchissement
de mémoire ne peut pas faire de mal. Donc, le public visé par cet article est
l'utilisateur débutant de Linux, quelqu'un qui n'a jamais écrit de script shell
mais qui désire devenir un Guru en 834657 étapes faciles. :)
Vu simplement, un script shell n'est rien de plus qu'un raccourci — une liste de commandes que vous auriez normalement tapées, les unes après les autres, pour les faire exécuter sur votre ligne de commande — avec une rien de magie pour faire savoir au shell que c'est en fait un script.
Cette magie se résume tout simplement à deux choses :
une annotation au début du script qui précise quel programme est utilisé pour l'exécuter et
un changement de permission du fichier le contenant, dans le but de le rendre exécutable.
Pour vous donner une exemple qui en plus sera utile, créons un script qui fera une sauvegarde (back up) d'un fichier dans un répertoire choisi. Nous allons détailler les différentes étapes nécessaires à une telle tâche.
Créons tout d'abord le fichier et positionnons ses permissions.
Tapez
touch bkup chmod +x bkup
La première ligne crée un fichier nommé bkup
dans le répertoire courant. La
seconde le rend exécutable. Remarquez que la commande chmod +x rend le
fichier exécutable par tout le monde. Si vous voulez limiter les droits
d'exécution vous devez utiliser chmod u+x ou chmod ug+x (référez vous
à la page de manuel de chmod). Toutefois, dans la plupart des cas, chmod +x
convient parfaitement.
A présent, nous devons effectivement créer le script. Ouvrez avec votre éditeur le fichier que vous venez de créer :
mcedit bkup
La toute première ligne des scripts que nous écrirons sera la suivante (encore
une fois, ne tenez pas compte du chiffre et du :
au début de la
ligne) :
1: #!/bin/bash
J'ai entendu appeler ça le hash-bang hack : la dièse-bidouille. Ce qui
est intéressant c'est que le signe dièse correspond en réalité à un début de
commentaire. Mais le doublet #!
est dans ce sens unique puisqu'il est
interprété comme un préfixe au nom de l'exécutable qui servira à interpréter les
lignes qui suivent.
C'est un point discret mais délicat puisque : quand un script s'exécute il lance en fait un nouveau processus bash qui est le fils du bash courant. Ce nouveau processus exécute le script et se termine, pour vous laisser dans les mains du processus bash qui lui avait donné naissance. Voilà pourquoi un script qui, par exemple, change de répertore alors qu'il s'exécute, ne vous y laissera pas une fois terminé : il n'a pas été demandé au shell original de changer de répertoire, et vous vous retrouvez exactement où vous étiez quand vous avez commencé. Bien sûr, il y a changement de répertoire pendant l'exécution du script.
Pour continuer avec notre script :
# "bkup" - copie les fichiers spécifiés dans le répertoire ˜/Sauvegarde de # l'utilisateur après avoir vérifié qu'il n'y ait pas de conflit de nom.
Comme je l'ai déjà dit, le caractère #
sert à délimiter un commentaire. Il
est judicieux de mettre des commentaires au début de chaque script (ou ailleurs)
pour vous souvenir exactement de ce qu'il fait et le différencier des nombreux autres que
vous aurez écrit. Dans de futurs articles nous verrons comment faire pour rendre
cet aide mémoire un peu plus automatique, mais poursuivons...
4: cp -i $1 ˜/Sauvegarde
L'option -i
de la commande cp la rend interactive ; c'est à dire que
si nous lançons la commande bkup fichier.txt et qu'un fichier fichier.txt
existe déjà dans le répertoire ˜/Sauvegarde
, cp vous demandera s'il
faut l'écraser (il mettra fin à l'opération si vous tapez autre chose que la
touche y — NdT : yes
, o
et oui
fonctionnent aussi. Chez moi en
tout cas).
Le $1
est un paramètre de position . Il fait référence à la
première chose qui est tapée après le nom du script. En fait, il existe une
liste complète de ces variables :
$0
le nom du script qui est exécuté ; dans ce cas bkup
,
$1
le premier paramètre ; dans ce cas fichier.txt
. De la même
manière il est possible de faire référence à n'importe quel paramètre en
utilisant la notation $<numéro>
.
$@
la liste complète de tous les paramètres : $1 $2 $3...
,
$#
le nombre de paramètres.
Il existe de nombreuses autres façons de faire référence aux paramètres de position et de les manipuler (voyez la page de manuel de bash), mais celle-là nous suffira pour l'instant.
Jusqu'à présent notre script ne fait pas grand chose. Ça vaut tout juste la
peine, non ? Bien, rendons-le un petit peu plus utile. Que faudrait-il faire si
vous vouliez à la fois conserver le fichier dans le répertoire
˜/Sauvegarde
et sauvegarder le nouveau ? Peut-être ajouter une
extension pour faire apparaître la « version » ? Essayons ça. Il
suffit d'ajouter une ligne et de modifier la dernière comme suit :
4: a=$(date +%T-%d_%m_%Y) 5: cp -i $1 ˜/Sauvegarde/$1.$a
Là, nous commençons à entrevoir la vrai puissance des scripts shell : la possibilité de se servir du
résultat d'autres commandes Linux. C'est ce qui est appelé la substitution
de commandes. La forme $(commande)
, permet d'exécuter la commande entre
parenthèses, puis de remplacer la chaîne "$(commande)"
par le résultat.
Notre exemple demande à la commande date d'écrire la date, l'heure et les
secondes du moment, puis d'affecter le résultat à la variable a
. Ensuite,
nous ajoutons la valeur de cette variable à la fin du nom de fichier qu'il
faudra sauvegarder dans ˜/Sauvegarde
. Remarquez que quand nous
affectons une valeur à une variable nous utilisons son nom (a=xxx
), mais
que quand nous voulons utiliser cette valeur, nous devons faire précéder le nom
de la variable du caractère $
($1.$a
). Le nom d'une variable peut être
n'importe quoi à l'exception :
de mots réservés :
case do done elif else esac fi for function if in select then until while time
de caractères ou métacaractères sans guillemets ou de caractères réservés :
! { } | & * ; ( ) < > space tab
de noms standards de variables shell comme :
PATH PS1 PWD RANDOM SECONDS
(lisez man bash pour connaître les autres).
D'après l'expérience que j'en ai, si vous vous contentez de noms de variables ne
contenant que des lettres minuscules, des -
et des _
, il n'y aura
aucun problème.
Les deux dernières lignes de ce script ont pour effet de créer un fichier
unique — quelque chose comme fichier.txt.01:00:00-01_01_2000
— qui ne
devrait pas entrer en conflit avec les fichiers déjà présents dans
˜/Sauvegarde
. Vous remarquerez que j'ai conservé l'option -i
comme une sécurité : si pour une raison des plus étrange, deux fichiers ont le
même nom, cp vous donnera une dernière chance de tout arréter. Dans le cas
contraire cela ne fera aucune différence et, comme la levure morte dans la
bière, ça ne fera pas de mal même si ça n'apporte rien.
À propos, l'ancienne version de la notation $(commande)
: `commande`
(remarquez les apostrophes inversées utilisées à la place des guillements) est
de plus en plus laissée de côté et ce pour une bonne raison. En effet, il est
plus facile de faire plusieurs niveaux de parenthèses. Par exemple :
$(cat $($2$(basename fichier1 txt)))
ne peut pas être écrit à l'aide d'apostrophes inversées, puisque la seconde serait considérée comme la fin de la première et la commande échouerai ou donnerai un résultat inattendu. Cependant vous pouvez tout de même les utiliser dans le cas de substitutions non imbriquées (ce qui est le cas le plus répandu), ou uniquement aux extrémités de la substitution. Vous éviterez ainsi tout problème.
Bien, voyons à présent ce que nous avons obtenu (avec l'ajout de quelques espaces et la suppression des numéros de ligne pour plus de lisibilité) :
#!/bin/bash # "bkup" - copie les fichiers spécifiés dans le répertoire ˜/Sauvegarde de # l'utilisateur après avoir vérifié qu'il n'y ait pas de conflit de nom. a=$(date +%T-%d_%m_%Y) cp -i $1 ˜/Sauvegarde/$1.$a
Bien sûr ce n'est qu'un script de deux ligne, mais de ceux qui savent se rendre utile. Nous continuerons à le modifier dans le prochain numéro.
Ah, oui, une dernière chose ; une autre FAQ d'Unix. Si vous essayez d'éxécuter votre script fraichement écrit en tapant simplement
bkup
vous aurez surement cette réponse bien connue :
bash: bkup: command not found
« Quoi ! Ne nous sommes nous pas donnés assez de mal ? Que se passe-t-il ? »
Contrairement à DOS, l'exécution des commandes et des scripts dans le répertoire
courant est désactivée par défaut. C'est une sécurité. Imaginez ce qui se
passerait si quelqu'un copiait, dans votre répertoire personnel, un script
appelé ls
et qui contiendrait rm -rf *
(tout effacer) et que vous
tapiez ls ! Si, dans votre variable PATH
, le répertoire courant .
était placé avant le répertoire bin
, vous auriez été vraiment triste...
Voilà pourquoi, entre autre, vous devez préciser le chemin de tous les
exécutables que vous désirez lancer dans le répertoire courant : une restriction
pleine de sagesse. Une fois que vous avez terminé de le modifier, vous pouvez
déplacer votre exécutable dans un répertoire qui est listé dans votre variable
PATH
. /usr/local/bin
> semble un bon candidat (Truc : tapez
echo $PATH pour connaître les répertoires qui en font partie).
En attendant, pour l'exécuter tapez simplement :
./bkup fichier.txt
où le ./
signifie que le fichier à exécuter est dans le répertoire
courant. Si vous l'invoquez de n'importe où ailleurs, utilisez plutôt
˜/
(NdT : souvenez vous que vos scripts sont à la racine de votre
répertoire personnel). Ce que je tente de mettre en avant ici est que vous
devez donner le chemin complet d'accès à votre exécutable, vu qu'il n'est dans
aucun des répertoires contenus dans le path
.
Cela laisse bien entendu supposer qu'un fichier appelé fichier.txt
est
présent dans le répertoire courant et que vous avez crée un sous-répertoire
appelé Sauvegarde
dans votre répertoire personnel. Si tel n'est pas le cas,
vous aurez un message d'erreur.
Dans cet article nous avons pris connaissance des opérations de base nécessaires à la création d'un script shell, mais également de principes précis :
création de fichier,
permissions,
sous-shells,
exécution dans un répertoire n'appartenant pas au PATH
,
la dièse-bidouille,
les commentaires,
les paramètres de position,
la substitution de commande,
les variables.
Voilà qui n'est pas mal pour un début. Jouez un peu avec, expérimentez. Les scripts shell représentent une grande part de la puissance et du côté attrayant de Linux. Le mois prochain nous parlerons de la gestion des erreurs — ce que votre script doit faire si la personne qui l'utilise fait des erreurs de syntaxe par exemple — mais également des boucles et des exécutions conditionnelles et aussi peut-être un peu des super outils qui sont utilisés en général dans les scripts shell.
N'hésitez pas à m'envoyer vos suggestions à propos de corrections ou d'améliorations, mais aussi à propos de vos trucs préférés dans vos scripts shell et des découvertes géniales que vous avez pû faire. Comme toute personne non submergée par son ego, je me considére un éternel étudiant, toujours prêt à apprendre quelque chose de nouveau. Si j'utilisais quelque chose dont vous m'auriez fait part, je vous citerai.
À la prochaine fois et Joyeux Linux-age.
Pages de manuel de bash, cp et chmod.
Pas à moi mon gars. Je lis le man de Bash chaque jour comme un Témoin de Jéhova lit la Bible. Non attend, le man de Bash est la Bible. Excuse moi... | ||
--En savoir plus sur les alias déroutants, tiré de comp.os.linux.misc. |
L'adaptation française de ce document a été réalisée dans le cadre du Projet de traduction de la Gazette Linux.
Vous pourrez lire d'autres articles traduits et en apprendre plus sur ce projet en visitant notre site : http://www.traduc.org/Gazette_Linux.
Si vous souhaitez apporter votre contribution, n'hésitez pas à nous rejoindre, nous serons heureux de vous accueillir.