tuteurs.ens.fr/unix/shell/script.tml
2005-09-07 23:00:03 +00:00

693 lines
20 KiB
XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
"tuteurs://DTD/tml.dtd">
<html>
<head>
<title>Scripts</title>
</head>
<body>
<h1><a name="programmation">Programmation de scripts en shell</a></h1>
<p>
Un shell, quel qu'il soit, peut exécuter des commandes prises dans un
fichier. Un fichier contenant des commandes pour le shell est appelé un
<em>script</em>. C'est en fait un <em>programme</em> écrit <em>dans le
langage du shell</em>. Ce langage comprend non seulement les commandes que
nous avons déjà vues, mais aussi des structures de contrôle
(constructions conditionnelles et boucles).
</p>
<p>
Pour la programmation du shell, nous allons utiliser le shell
<code>sh</code>, qui est le plus répandu et standard. Ce que nous avons
vu jusqu'ici s'applique aussi bien à <code>sh</code> qu'à
<code>zsh</code> et aussi à <code>csh</code>, à quelques exceptions
près, que nous vous signalerons en temps voulu. </p>
<h2>Créer un script</h2>
<h3>Écrire un script</h3>
<p>
Un script shell est un fichier en mode texte. C'est-à-dire que ce n'est
pas un fichier binaire, exécutable directement par la machine, mais
<strong>il doit être interprété</strong>.
</p>
<h4>L'interprétation d'un script</h4>
<p>
L'interprétation signifie que chaque commande contenue dans un script
doit être lue par un programme, appelé <em>interpréteur</em> (et non
interprète, bizarrement)&nbsp;; l'interpréteur analyse chaque commande
du script et la traduit «&nbsp;à la volée&nbsp;» en langage machine, ce
qui permet l'exécution du script.
</p>
<div class="encadre">
On oppose l'interprétation à la compilation, dans laquelle le programme
est traduit <em>une fois pour toutes</em> en langage machine, quel que
soit le nombre de ses exécutions&nbsp;; tandis que le programme
interprété est traduit à la volée pour chacune de ses exécutions. Par
exemple, le langage&nbsp;C est un langage compilé.
</div>
<p>Dans le cas des scripts shell, l'interpréteur, c'est le shell
lui-même. Dans d'autres langages, comme le Perl, l'interpréteur est un
programme indépendant du shell. </p>
<h4>L'édition d'un script</h4>
<p>Un script étant un fichier en mode texte, il doit être créé avec un
éditeur de texte. Un éditeur de texte est un programme dont la fonction
est... d'éditer du texte. Pour savoir plus à leur sujet, consultez les
pages sur les <a href="&url.tuteurs;unix/editeurs/">éditeurs</a>. </p>
<p>Mais quel éditeur choisir&nbsp;?</p>
<p> Tout d'abord, il faut savoir que <strong>n'importe quel éditeur est
capable d'ouvrir et d'écrire des scripts shell</strong>, et vous pouvez
tout à fait modifier avec n'importe quel éditeur de texte ce que vous
avez écrit avec n'importe quel autre.</p>
<p>Mais il faut savoir aussi que <strong>certains éditeurs de texte sont
plus appropriés que d'autres à l'écriture de scripts shell</strong>. Par
exemple, <code>nano</code> permet d'éditer des scripts comme tout autre
éditeur, mais quand un script fait plus d'une dizaine de lignes, on
commence à s'y perdre un peu. À l'inverse, <a
href="&url.tuteurs;unix/editeurs/emacs.html"><code>emacs</code></a> et
<a href="&url.tuteurs;unix/editeurs/vim.html"><code>vim</code></a>
offrent quelques fonctionnalités qui deviennent rapidement
indispensables&nbsp;:</p>
<ol>
<li> l'indentation&nbsp;;</li>
<li> la coloration syntaxique.</li>
</ol>
<h5>L'indentation</h5>
<p>L'indentation consiste à «&nbsp;aérer&nbsp;» votre texte selon sa
construction logique. C'est très utile, en particulier, quand on a un
script qui ressemble à ceci&nbsp;:</p>
<pre>
#!/bin/sh
# Fichier &quot;vote-nir&quot;
echo &quot;Êtes-vous favorable au remplacement du NIR par le VIR&nbsp;?&quot;
select opinion in Pour Contre
do case $opinion in
# Laisser passer ceux qui répondent correctement à la question
&quot;Pour&quot;|&quot;Contre&quot;) break;;
# Au cas où des zozos tapent sur autre chose que 1 ou 2
&quot;*&quot;) continue;;
esac
done
# M'envoyer le résultat par mail
echo &quot;$opinion&quot; | mail bourdieu
</pre>
<p>Même (surtout) si vous ne comprenez pas ce que tout cela veut dire,
vous conviendrez que ce n'est pas très lisible. Comparez donc avec
ceci&nbsp;: </p>
<pre>
#!/bin/sh
# Fichier &quot;vote-nir&quot;
echo &quot;Êtes-vous favorable au remplacement du NIR par le VIR&nbsp;?&quot;
select opinion in Pour Contre
do
case $opinion in
# Laisser passer ceux qui répondent correctement à la question
&quot;Pour&quot;|&quot;Contre&quot;) break;;
# Au cas où des zozos tapent sur autre chose que 1 ou 2
&quot;*&quot;) continue;;
esac
done
# M'envoyer le résultat par mail
echo &quot;$opinion&quot; | mail bourdieu
</pre>
<p>Les deux scripts sont interprétés exactement de la même façon&nbsp;:
l'interpréteur ignore les espaces et les lignes vides. Mais avec
l'indentation, on perçoit immédiatement (en tout cas, beaucoup plus
vite) la <strong>structure logique</strong> du script.</p>
<h5>La coloration syntaxique</h5>
<p>Les éditeurs comme <a
href="&url.tuteurs;unix/editeurs/emacs.html"><code>emacs</code></a> et
<a href="&url.tuteurs;unix/editeurs/vim.html"><code>vim</code></a>
analysent automatiquement le statut des différents mots et symboles que
vous tapez et les colorent logiquement. Par exemple, avec emacs, vous
pouvez avoir&nbsp;:</p>
<ul>
<li> les commentaires en rouge&nbsp;;</li>
<li> les noms de commande en bleu foncé&nbsp;;</li>
<li> les noms d'arguments en vert&nbsp;;</li>
<li> les noms de variables en jaune&nbsp;;</li>
<li> les instructions liées aux boucles en bleu clair&nbsp;;</li>
<li> etc.</li>
</ul>
<p>Ça n'a l'air de rien, dit comme cela, mais comparez vous-même et vous
verrez que ces outils sont indispensables, et que l'on y gagne au moins
la moitié du temps d'écriture et de débugage.</p>
<h5>Apprendre <code>emacs</code> ou <code>vim</code></h5>
<p>Apprenez donc, si ce n'est pas déjà le cas, les commandes de base
d'emacs ou de vim, ce sont des outils quasi incontournables au
programmeur shell, surtout débutant.</p>
<p> Heureusement, les tuteurs vous ont concocté des pages
d'initiation&nbsp;: le <a
href="&url.tuteurs;unix/editeurs/emacs.html">tutorial
<code>emacs</code></a> et le <a
href="&url.tuteurs;unix/editeurs/vim.html">tutorial
<code>vim</code></a>.</p>
<p>Vous hésitez entre <code>emacs</code> et <code>vim</code>&nbsp;? Tout
le monde est passé par là. Jetez un coup d'&oelig;il à chacun des deux,
puis concentrez-vous sur celui qui vous paraît le plus sympathique et le
plus pratique&nbsp;; et si vous hésitez encore, tirez-en un au
sort, ils se valent vraiment.</p>
<h3>Rendre un script exécutable</h3>
<p> Pour que le shell sache comment l'interpréter, un script shell doit
commencer par la ligne: </p>
<pre>#!/bin/sh</pre>
<p>
Il doit aussi avoir être exécutable (bit <code>x</code>). Le
<code>#!/bin/sh</code> sur la première ligne indique que ce script doit être
exécuté par le shell <code>sh</code> dont on indique le chemin
d'accès. Pour rendre un fichier exécutable, tapez&nbsp;:
</p>
<pre><span class="prompt">chaland ~</span> chmod u+x fichier
</pre>
<p class="continue">
(pour en savoir plus sur les droits attachés à un fichier, consultez la
page sur <a href="&url.tuteurs;unix/droits.html">les droits
d'accès</a>).
</p>
<h3>Le chemin d'une commande</h3>
<p>
Pour comprendre ce qui suit, vous devez savoir ce qu'est le
<code>PATH</code>. Si ce n'est pas le cas, lisez la <a
href="presentation.html">présentation du shell</a>.
</p>
<p>
Quand vous exécutez un script, vous pouvez vous trouver à
n'importe quel endroit de l'arborescence de vos répertoires. Si le
répertoire courant ne se situe pas dans votre <code>PATH</code> et
que vous voulez exécuter un programme qui s'y trouve, vous ne pouvez
pas taper&nbsp;:
</p>
<pre>
<span class="prompt">clipper ~</span> commande
</pre>
<p class="continue">car si le répertoire courant n'est pas dans le
<code>PATH</code>, le shell n'ira pas y chercher
<code>commande</code>. </p>
<p>
Vous recevrez donc un message comme&nbsp;:
</p>
<pre>
<span class="prompt">clipper ~</span> commande
zsh: command not found: commande</pre>
<h4>Spécifier le chemin d'une commande</h4>
<p>
Pour que le shell comprenne où chercher votre commande, il faut donc
spécifier l'emplacement de la commande en donnant son chemin, qu'il
soit absolu&nbsp;:
</p>
<pre>
<span class="prompt">clipper ~</span> /home/toto/repertoire/courant/commande
</pre>
<p class="continue">ou relatif&nbsp;: </p>
<pre>
<span class="prompt">clipper ~</span> repertoire/courant/commande
</pre>
<p class="continue">ou encore sous la forme&nbsp;: </p>
<pre>
<span class="prompt">clipper ~/repertoire/courant</span> ./commande
</pre>
<h4>Le répertoire <code>~/bin</code></h4>
<p>
Il y a un certain nombre de commandes que l'on peut vouloir utiliser
depuis n'importe quel répertoire. Dans ce cas, il est fastidieux de&nbsp;:
</p>
<ul>
<li> devoir se rappeler dans quel répertoire se trouve chacune
d'entre elles&nbsp;;</li>
<li> devoir taper à chaque fois le chemin de la commande.</li>
</ul>
<p>Il suffit donc de mettre tous vos scripts dans un même
répertoire, et de mettre ce répertoire dans le
<code>PATH</code>. Par convention, ce répertoire s'appelle
<code>bin</code> et se place dans votre répertoire personnel. Si
votre répertoire personnel est <code>/home/toto</code>, ce
répertoire sera donc <code>/home/toto/bin</code>. </p>
<p>
Commencez donc par créer ce répertoire&nbsp;:
</p>
<pre>
<span class="prompt">clipper ~</span> mkdir bin
</pre>
<p>
Ensuite, vérifiez qu'il soit bien dans votre <code>PATH</code>&nbsp;:
</p>
<pre>
<span class="prompt">clipper ~</span> echo $PATH
</pre>
<p class="continue">Si vous voyez par exemple <code>$HOME/bin</code>
dans votre <code>PATH</code>, alors c'est bon, tous les fichiers
exécutables situés dans ce répertoire seront accessibles depuis
n'importe quel répertoire.
</p>
<p>
Si ce n'est pas le cas, il faut ajouter ce répertoire au
<code>PATH</code>. Pour cela, ajoutez dans le fichier de configuration
de votre shell, par exemple le fichier <code>.zshrc</code>, la
ligne&nbsp;:
</p>
<pre>PATH=$PATH:$HOME/bin</pre>
<p class="continue">Cette ligne indique que la prochaine fois que vous
ouvrirez votre shell, le répertoire <code>bin</code> figurera dans
votre <code>PATH</code>.
</p>
<h2>Principes généraux des scripts shell</h2>
<h3>Une succession de commandes</h3>
<p> Si vous manipulez déjà le shell en ligne de commande, vous
pouvez commencer vos premiers scripts. Un script shell est en effet
avant tout une <strong>succession de commandes</strong>.
</p>
<p> Par exemple, si vous avez coutume de taper successivement, quand
vous vous loguez à l'ENS&nbsp;:
</p>
<pre>
<span class="prompt">clipper ~</span> mozilla &amp;
<span class="prompt">clipper ~</span> mutt
</pre>
<p class="continue">vous pouvez vous créer le script suivant dans le
fichier <code>~/bin/amorce</code>&nbsp;:</p>
<pre>
#!/bin/sh
mozilla &amp;
mutt
</pre>
<p>Ainsi, dès que vous vous connectez, vous pouvez taper
<code>amorce</code> dans le shell, et vos commandes s'exécuteront
automatiquement. </p>
<h3>Commentaires</h3>
<p>
Presque tous les langages informatiques autorisent d'insérer des
commentaires&nbsp;; le shell n'échappe pas à la règle. Pour
cela, il suffit de faire précéder chaque ligne de commentaire du
caractère «&nbsp;#&nbsp;». Exemple&nbsp;:
</p>
<pre>
#!/bin/sh
# Tout ce que j'écris ici ne sera pas lu.
echo &quot;Ce que je tape ici sera lu.&quot;
</pre>
<p>
Les lignes de commentaire sont tout bonnement ignorées par
l'interpréteur. Alors, allez-vous demander, à quoi servent-elles si
elles ne servent à rien&nbsp;? Elles sont indispensables pour tout
programmeur, car elles lui permettent de «&nbsp;commenter&nbsp;» son
programme, c'est-à-dire d'écrire ce qu'il veut, comme dans la marge d'un
livre.
</p>
<p>
Les commentaires jouent pour beaucoup dans la lisibilité d'un programme
par un humain. Car les lignes de commande pures sont relativement
austères&nbsp;; des commentaires permettent de les décrire à l'intention
d'un être humain.
</p>
<p>
Sans doute allez-vous vous demander quel être humain cela peut bien
intéresser. Eh bien, quelqu'un d'autre que vous qui lit ce code&nbsp;;
ou bien vous-même, dans un mois, un an, une décennie, ou plus, quand
vous aurez tout oublié de ce programme et de son fonctionnement.
</p>
<p>
N'hésitez donc pas à recourir abondamment aux commentaires, qui
accroissent la lisibilité de votre programme, même s'ils n'ont
absolument aucune influence directe sur son fonctionnement intrinsèque.
</p>
<h3>Lignes blanches</h3>
<p>
Les lignes blanches ne sont pas interprétées non
plus. N'hésitez donc surtout pas à espacer votre script, les
lignes blanches ne consomment presque rien en termes d'espace disque, ce
n'est donc pas une ressource rare&nbsp;; et elles facilitent
considérablement la lecture pour un être humain.
</p>
<h3>L'impératif de lisibilité</h3>
<p>
Pourquoi espacer son script&nbsp;? Pourquoi insérer des
commentaires&nbsp;? Pour une seule et même raison&nbsp;:
<strong>votre script doit être lisible</strong>. Pourquoi être
lisible&nbsp;? </p>
<p> D'abord, pour autrui&nbsp;: si d'autres gens lisent votre script, il
doit être intelligible, et les passages complexes doivent être
explicités par des commentaires.</p>
<p> Ensuite, pour vous-même&nbsp;; au moment où vous écrivez un
script, vous comprenez tout, naturellement&nbsp;; mais si vous le
relisez dans quelques mois, voire quelques années, les passages
obscurs risqueront d'être incompréhensibles, ce qui est
particulièrement pénible quand on essaye de <em>débuguer</em>
un programme, c'est-à-dire d'en corriger les erreurs.
</p>
<h2>Un script qui parle&nbsp;: la commande <code>echo</code></h2>
<p>
Maintenant que vous savez comment on peut exécuter un script, il s'agit
de le remplir... Commençons par ce qu'il y a de plus simple&nbsp;:
afficher du texte.
</p>
<h3>Hello World&nbsp;!</h3>
<p>
Traditionnellement, on commence par faire un programme qui affiche
simplement la ligne «&nbsp;Hello world&nbsp;» (ce qui signifie en
anglais&nbsp;: bonjour au monde entier). Faites donc un fichier
<code>helloworld</code> contenant les lignes suivantes&nbsp;:
</p>
<pre>
#!/bin/sh
# Fichier &quot;helloworld&quot;
echo &quot;Hello world&quot;
</pre>
<p>
Exécutez ensuite ce programme, par exemple en tapant, dans le répertoire
où il se trouve&nbsp;:
</p>
<pre><span class="prompt">clipper ~ $</span> ./helloworld
Hello world</pre>
<p>
Ça y est, vous avez créé votre premier programme&nbsp;! Lancez-le autant
de vous que vous voulez, vous avez bien mérité d'être fier de vous.
</p>
<p>
Exercice&nbsp;: francisez ce script.
</p>
<h3>La commande <code>echo</code></h3>
<p>
La commande <code>echo</code> sert à afficher du texte. Chaque ligne de
texte est écrite sur une ligne à part. Exemple&nbsp;:
</p>
<pre>
#!/bin/sh
# Fichier &quot;bonjour&quot;
echo &quot;Bonjour... &quot;
echo &quot;Comment allez-vous ?&quot;
</pre>
<p class="continue">affiche les lignes suivantes&nbsp;:</p>
<pre>
Bonjour...
Comment allez-vous ?
</pre>
<p class="continue">et non&nbsp;:</p>
<pre>
Bonjour... Comment allez-vous ?
</pre>
<h4>Annuler le retour chariot</h4>
<p>
Si vous voulez annuler le retour chariot qui a lieu par défaut à la fin
de toute commande <code>echo</code>, il faut utiliser l'option
<code>-n</code>. Le programme sera alors&nbsp;:
</p>
<pre>
#!/bin/sh
echo -n &quot;Bonjour...&quot;
echo &quot;Comment allez-vous ?&quot;
</pre>
<p class="continue">
Alors seulement, vous pourrez avoir&nbsp;:
</p>
<pre>
Bonjour... Comment allez-vous ?
</pre>
<h4>Citer des variables</h4>
<p>Faisons mieux encore&nbsp;: votre script va citer des variables. Pour
savoir ce que sont des variables, allez voir la page sur les <a
href="variable.html">variables</a>.</p>
<p>La variable <code>USER</code> contient le login de
l'utilisateur&nbsp;; la variable <code>PWD</code> (pour <em>print
working directory</em>) affiche le répertoire courant. Faisons donc le
script suivant&nbsp;:</p>
<pre>
#/bin/sh
# Fichier &quot;mon-pwd&quot;
echo &quot;Bonjour $USER...&quot;
echo &quot;Tu es actuellement dans le répertoire $PWD.&quot;
</pre>
<p>Comme vous pouvez le remarquer, pour citer le contenu d'une variable,
on ajoute le signe dollar ($) devant son nom.</p>
<h2>Un script qui écoute&nbsp;: la commande <code>read</code></h2>
<p>Parler c'est bien, écouter c'est mieux. Jusqu'ici, votre programme
est capable de parler, de dire bonjour, mais il n'est même pas capable
de vous appeler par votre nom, tout juste par votre login, ce qui n'est
pas très intime...</p>
<p>
Nous allons donc lui donner la faculté d'écouter, grâce à la commande
<code>read</code>. Prenons le script suivant&nbsp;:
</p>
<pre>
#!/bin/sh
# Fichier &quot;mon-nom&quot;
echo &quot;Bonjour... Comment vous-appelez-vous ?&quot;
read nom
echo &quot;Je vous souhaite, $nom, de passer une bonne journée.&quot;
</pre>
<p>Vous connaissez déjà la commande <code>echo</code>. La commande
<code>read</code> permet de lire des variables. Si vous exécutez ce
script, après avoir affiché la ligne </p>
<pre>
Bonjour... Comment vous-appelez-vous ?
</pre>
<p class="continue">le shell va attendre que vous tapiez votre
nom. Tapez par exemple <code>Toto</code>, puis appuyez sur Entrée, et
vous verrez&nbsp;:</p>
<pre>
Bonjour... Comment vous-appelez-vous ?
<em>Toto</em>
Je vous souhaite, Toto, de passer une bonne journée.</pre>
<div class="attention">
La commande <code>read</code> doit être suivie du seul nom de la
variable, <strong>non précédé du signe dollar</strong>. <em>Le signe
dollar ne doit précéder le nom de la variable que lorsque l'on cite son
contenu.</em>
</div>
<h3>Lire plusieurs variables</h3>
<p>
La commande <code>read</code> permet également de lire plusieurs
variables. Il suffit pour cela d'indiquer à la suite les noms des
différentes variables. Exemple&nbsp;:
</p>
<pre>
#!/bin/sh
# Fichier &quot;administration&quot;
echo &quot;Écrivez votre nom puis votre prénom :&quot;
read nom prenom
echo &quot;Nom : $nom&quot;
echo &quot;Prénom : $prenom&quot;
</pre>
<p>Vous aurez&nbsp;:</p>
<pre>
Écrivez votre nom puis votre prénom :
<em>Hugo Victor</em>
Nom : Hugo
Prénom : Victor
</pre>
<h3>Appuyer sur Entrée pour continuer</h3>
<p>Nous avons vu comment utiliser <code>read</code> avec un seul
argument et avec plusieurs arguments&nbsp;; il reste à voir l'usage de
<code>read</code> <em>sans</em> argument. Oui, c'est possible&nbsp;!
Cela équivaut simplement à attendre un réaction de l'utilisateur, mais
sans mémoriser ce qu'il tape.</p>
<p> Concrètement, cela est très utile après un message «&nbsp;Appuyez
sur Entrée pour continuer.&nbsp;» Exemple&nbsp;:</p>
<pre>
#!/bin/sh
# Fichier &quot;continuer&quot;
echo &quot;Quelle est la différence entre un canard ?&quot;
echo &quot;(Appuyez sur Entrée pour avoir la réponse)&quot;
read
echo &quot;Les pattes, surtout la gauche.&quot;
</pre>
<h2>Poursuivre l'apprentissage de la programmation shell</h2>
<p>
Vous connaissez maintenant les bases de la programmation en shell. Vous
avez déjà de quoi écrire de nombreux scripts qui peuvent vous faciliter
la vie de tous les jours. Pour cela, lorsque vous ressentez un besoin,
ne vous demandez pas si vous avez les connaissances requises pour écrire
un programme donné, mais plutôt <em>comment, à partir de vos
connaissances actuelles, vous pourriez le réaliser</em>.
</p>
<p>
Pour poursuivre votre apprentissage, vous pouvez passer à la lecture des
pages suivantes&nbsp;:
</p>
<ul>
<li> <a href="entreesortie.html">entrée, sortie, redirection</a>&nbsp;:
faire interagir les éléments du shell pour construire des programmes
évolués&nbsp;;</li>
<li> <a href="test.html">test et calcul arithmétique</a>&nbsp;:
manipuler les valeurs de vrai et de faux pour orienter le parcours d'un
programme&nbsp;;</li>
<li> <a href="boucle.html">boucles et structures de contrôle</a>&nbsp;:
maîtriser l'aiguillage d'un programme&nbsp;;</li>
<li> <a href="fonction.html">définir et appeler des fonctions</a>&nbsp;:
alléger ses programmes et améliorer leur qualité en les concevant d'une
façon modulaire.</li>
</ul>
<p>
Ou bien vous pouvez revenir à la <a href="index.html">page centrale sur
le shell</a>, d'où vous pourrez vous orienter vers d'autres parties du
cours.
</p>
<div class="metainformation">
Auteur&nbsp;: Baptiste Mélès.
Dernière modification le <date value="$Date: 2005-09-07 23:00:04 $" />.
</div>
</body>
</html>