Bap: Ajout au CVS : une erreur de conscrit...

This commit is contained in:
meles 2005-05-31 11:33:37 +00:00
parent aaf4f2e59a
commit 7c56ab2bd4
5 changed files with 1789 additions and 0 deletions

500
unix/shell/commande.tml Normal file
View file

@ -0,0 +1,500 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
"tuteurs://DTD/tml.dtd">
<html>
<head>
<title>Commandes</title>
</head>
<body>
<h1>Le shell en ligne de commande</h1>
<h2>Créer des processus</h2>
<p> Le shell a la faculté de lancer de nombreux programmes, sinon
tous. C'est une des raisons pour lesquelles on peut dire que c'est un
outil extrêmement puissant.
</p>
<h3>Qu'est-ce qu'un processus&nbsp;?</h3>
<p> Lancer un programme, c'est créer un
<em>processus</em>. Tautologisons&nbsp;: <strong>un processus est une
instance d'exécution d'un programme</strong>, c'est-à-dire
l'exécution particulière d'un programme universel (<i>i.e</i> qui
n'est pas réductible à l'une de ses exécutions). </p>
<p>Par exemple, si je lance Mozilla, un processus se crée, du nom de
<code>mozilla</code>. Ensuite, sans fermer ce programme, je lance une
deuxième fois Mozilla&nbsp;; un nouveau processus se crée, du nom
de <code>mozilla</code> également. J'ai donc deux processus
distincts, même s'ils concernent le même programme. Et à chaque
fois que je lance un programme, un processus se crée.
</p>
<h3>Généalogie des processus</h3>
<p> Dans notre exemple, nous avons deux processus
<code>mozilla</code>. Ils sont indépendants l'un de l'autre&nbsp;:
quoi qu'il puisse se passer dans l'un, cela ne devrait pas influer sur
le comportement de l'autre.
</p>
<p> Toutefois, s'ils sont indépendants l'un de l'autre, tous deux
dépendent du processus qui les a lancés. Eh oui, <strong>le shell
est un programme comme un autre, donc chacune de ses exécutions
correspond également à la création d'un processus</strong>. Si
vous lancez trois shells en même temps, par exemple dans trois
terminaux différents, vous aurez trois processus <code>shell</code>
distincts. </p>
<p>
Les processus entretiennent entre eux des relations de parenté. Mais
comme les processus ont tous le même sexe, <strong>chaque processus
n'a qu'un parent. Un processus peut avoir plusieurs enfants</strong>.
</p>
<p> Le shell est, comme nous venons de le voir, <strong>un processus qui
peut enfanter d'autres processus</strong>. Par exemple, à chaque fois
que je tape <code>mozilla</code>, mon shell crée un processus
<code>mozilla</code>. </p>
<h3>Lancer des processus en arrière-plan</h3>
<p>
<strong>Par défaut, le shell met le processus enfant en
avant-plan</strong>. Cela veut dire que si je tape simplement&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~</span> mozilla
</pre>
<p class="continue">
le processus <code>mozilla</code> prend la main, et le shell ne
réagit plus (il n'affiche même plus l'invite de commande) avant
la fin du processus enfant. Ce n'est que lorsque le processus
<code>mozilla</code> prend fin (si je ferme ce programme, par exemple)
que le shell reprend la main.
</p>
<p>Ce comportement par défaut n'est parfois pas très pratique. Par
exemple, si je veux lancer Mozilla, mais aussi Emacs et d'autres
programmes qui s'ouvrent dans leur propre fenêtre, je suis obligé
d'ouvrir à chaque fois un nouveau terminal avec un nouveau shell, et
ce uniquement pour y taper une commande, après quoi le shell se
retrouve inutilisable jusqu'à la fin de chacun de ces
programmes. </p>
<p>Il faudrait donc que l'on puisse <strong>lancer des processus enfants en
arrière-plan</strong>, de telle sorte que ces processus se lancent
dans leur propre fenêtre et me permettent toutefois de dialoguer avec
le shell sans avoir à&nbsp;:</p>
<ul>
<li> ouvrir un nouveau shell dans une nouvelle fenêtre&nbsp;;</li>
<li> attendre la fin de l'un de ces processus.</li>
</ul>
<p>Pour cela, il faut ajouter le signe «&nbsp;&amp;&nbsp;» à la
fin d'une ligne de commande. On peut ainsi taper&nbsp;:</p>
<pre>
<span class="prompt">chaland ~</span> mozilla &amp;
<span class="prompt">chaland ~</span> mozilla &amp;
<span class="prompt">chaland ~</span>
</pre>
<p class="continue">et de la sorte, un même shell peut engendrer
autant de processus que vous voulez.</p>
<h3>Émanciper un processus enfant</h3>
<p>La vie est déjà plus pratique comme cela. Mais parfois, on
aimerait que le processus enfant survive au processus parent. Car par
défaut, <strong>la mort d'un processus parent entraîne celle de
tous ses enfants</strong>. Par exemple, si le shell qui a enfanté les
deux processus <code>mozilla</code> vient à décéder (si je le
ferme brutalement, ou s'il plante), les deux processus
<code>mozilla</code> le suivront dans la mort. </p>
<p>
Il faudrait donc qu'un processus parent puisse émanciper des
processus enfants, pour que sa propre mort n'entraîne pas les
leurs. Pour cela, il faut utiliser les symboles
«&nbsp;&amp;!&nbsp;». Par exemple, si je tape&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~</span> mozilla &amp;!
<span class="prompt">chaland ~</span> mozilla &amp;!
<span class="prompt">chaland ~</span>
</pre>
<p class="continue">puis que je ferme brutalement le shell en tapant
Ctrl-D et en ignorant d'éventuels avertissements, le shell
disparaîtra, mais pas les processus <code>mozilla</code>. </p>
<h2>Arguments et options</h2>
<p> Jusqu'ici, l'exemple de processus enfant que nous avons pris
était relativement simple&nbsp;: il s'agissait de la commande
<code>mozilla</code>, que l'on peut lancer en tapant simplement son
nom. </p>
<p>
Jusqu'ici, on peut se dire que la ligne de commande, c'est comme quand
on clique sur une icône, sauf que c'est plus fatigant. Effectivement,
pourquoi se fatiguer à taper une suite de caractères quand il
suffit d'appuyer une ou deux fois sur le bouton d'une souris&nbsp;?
</p>
<p>
C'est dans l'usage des arguments et des options que la ligne de commande
commence à révéler une partie de sa puissance, et de sa nette
supériorité sur les interfaces graphiques.
</p>
<h3>Les arguments</h3>
<p>Un argument est une suite de caractères que l'on donne à une
commande et qui lui indique comment se comporter. Les options sont un
type d'argument. </p>
<h4>Exemples</h4>
<p>Par exemple, si je veux ouvrir le fichier <code>hermocrate.txt</code>
avec l'éditeur de texte <code>nano</code>, j'ai le choix entre deux
possibilités&nbsp;:</p>
<ul>
<li> ou bien je tape&nbsp;:
<pre><span class="prompt">chaland ~</span> nano </pre>
puis Ctrl-R pour indiquer d'ouvrir un fichier, puis Ctrl-T
pour voir la liste des fichiers, puis les flèches pour choisir mes
fichiers, enfin Entrée pour sélectionner
<code>hermocrate.txt</code>&nbsp;&mdash; mais tout cela est bien
fastidieux&nbsp;; </li>
<li> ou bien je tape directement&nbsp;:
<pre><span class="prompt">chaland ~</span> nano hermocrate.txt</pre>
</li>
</ul>
<p>Ou encore, pour reprendre l'exemple de Mozilla, si je veux aller voir
la page des tuteurs et que ce n'est pas ma page d'accueil par défaut
(ce qui est un tort&nbsp;!), je peux taper&nbsp;:</p>
<pre><span class="prompt">chaland ~</span> mozilla http://www.tuteurs.ens.fr/</pre>
<h3>Les options</h3>
<p>
Les options sont un cas particulier d'arguments, qui donnent au
programme que l'on exécute toutes sortes d'information, souvent
plus ou moins périphériques, en général moins importantes
que les arguments.
</p>
<p>Sous Unix, les options commencent généralement par un
tiret. Certaines options peuvent à leur tour prendre des
arguments.</p>
<p>Par exemple, si l'on veut ouvrir un fichier au format MS-DOS, on peut
taper&nbsp;:</p>
<pre><span class="prompt">chaland ~</span> nano -D hermocrate.txt</pre>
<h4>Options à arguments</h4>
<p>
L'option <code>-r</code> de <code>nano</code> prend un argument&nbsp;;
ainsi, si l'on veut que Nano coupe les lignes au bout de 75
caractères, on doit ouvrir Nano en tapant&nbsp;:
</p>
<pre><span class="prompt">chaland ~</span> nano -r 75 hermocrate.txt</pre>
<h4>Options à nom long</h4>
<p>Traditionnellement, et pur des raisons de concision, la majorité
des options, sous Unix, est composée d'un tiret et d'une seule
lettre. Toutefois, pour des raisons pratiques, il peut également
exister des noms d'option longs&nbsp;; ils sont en général
précédés, non d'un, mais de deux tirets. Exemple&nbsp;:</p>
<pre><span class="prompt">chaland ~</span> nano &#45;&#45;fill=75 hermocrate.txt</pre>
<p class="continue">Dans cet exemple, il se trouve que l'option à nom
long <code>&#45;&#45;fill=</code> équivaut à l'option à nom
court <code>-r</code>. Mais toutes les commandes courtes n'ont pas
systématiquement leur équivalent à nom long, et vice versa.</p>
<h2>Jokers et citations</h2>
<h3><a name="raccourcis">Raccourcis pour les noms de fichiers&nbsp;: les
jokers</a></h3>
<p>
Il est souvent ennuyeux d'avoir à taper une longue liste de fichiers
pour les donner en arguments à une commande, comme&nbsp;:
</p>
<pre>cc -o foo bar.c gee.c buz.c gog.c</pre>
<p>
Pour éviter ces problèmes, on peut utiliser des <em>jokers</em>
(<em>wildcards</em> en anglais). Pourquoi ce nom&nbsp;? Eh bien, dans
certains jeux de cartes, le joker permet de remplacer n'importe quelle
carte&nbsp;; dans le shell, les jokers permettent de remplacer n'importe
quel caractère ou n'importe quelle séquence de caractères.
</p>
<p>
Il existe principalement trois types de jokers&nbsp;:
</p>
<ul>
<li> l'étoile (*), qui remplace n'importe quelle séquence de
caractères&nbsp;;</li>
<li> le point d'interrogation, qui remplace n'importe quel
caractère&nbsp;;</li>
<li> les crochets ([]), qui remplacent n'importe quel caractère d'une
liste déterminée.</li>
</ul>
<h4>L'étoile : *</h4>
<p>
Une <strong>étoile</strong> <code>*</code> dans un nom de fichier est
interprétée par le shell comme « n'importe quelle séquence de
caractères » (mais ça ignore les fichiers dont le nom commence par un
point). Exemple&nbsp;:
</p>
<pre>cc -o foo *.c</pre>
<p>
Pour interpréter l'étoile, le shell va faire la liste de tous les noms de
fichiers du répertoire courant qui ne commencent pas par <code>.</code> et
qui finissent par <code>.c</code>. Ensuite, il remplace <code>*.c</code>
par cette liste (triée par ordre alphabétique) dans la ligne de commande, et
exécute le résultat, c'est-à-dire par exemple :
</p>
<pre>cc -o foo bar.c buz.c foo.c gee.c gog.c</pre>
<h4>Le point d'interrogation : ?</h4>
<p>
On a aussi le <strong>point d'interrogation</strong> <code>?</code>, qui
remplace un (et exactement un) caractère quelconque (sauf un point en début
de nom). Par exemple, <code>ls *.?</code> liste tous les dont l'extension
ne comporte qu'un caractère (<code>.c</code>, <code>.h</code>...).
</p>
<h4>Les crochets : []</h4>
<p>
La forme <code>[abcd]</code> remplace un caractère quelconque parmi
<code>a</code>, <code>b</code>, <code>c</code>, <code>d</code>. Enfin,
<code>[^abcd]</code> remplace un
caractère quelconque qui ne se trouve pas parmi <code>a</code>, <code>b</code>,
<code>c</code>, <code>d</code>.
</p>
<h4>Exemple</h4>
<pre>echo /users/*</pre>
<p class="continue">
affiche à peu près la même chose que
</p>
<pre>ls /users</pre>
<p class="continue">
(La commande <code>echo</code> se contente d'afficher ses arguments.)
</p>
<div class="attention">
<p>
&icone.attention; Attention :
</p>
<ul>
<li><strong>C'est le shell qui fait le remplacement des arguments
contenant un joker</strong>. On ne peut donc pas faire <code>mv *.c
*.bak</code>, car le shell va passer à <code>mv</code> les arguments
<code>foo.c bar.c foo.bak bar.bak</code>, et <code>mv</code> ne sait pas
quel fichier remplacer. </li>
<li> <strong>Attention aux espaces</strong>. Si vous tapez <code>rm *
~</code>, le shell remplace l'étoile par la liste des fichiers présents,
et ils seront tous effacés. Si vous tapez <code>rm *~</code>, seuls les
fichiers dont le nom finit par un tilde seront effacés. </li>
</ul>
</div>
<div class="encadre">
<p>
<strong>Interlude&nbsp;:</strong> comment effacer un fichier nommé
<code>?*</code> ? On ne peut pas taper <code>rm ?*</code> car le shell
remplace <code>?*</code> par la liste de tous les fichiers du répertoire
courant. On peut taper <code>rm -i *</code> qui supprime tous les
fichiers, mais en demandant confirmation à chaque fichier. On répond
<code>n</code> à toutes les questions sauf <code>rm: remove  ?*</code>.
Autre méthode: utiliser les mécanismes de citation.
</p>
</div>
<h3><a name="quotation">Contourner les jokers&nbsp;: la citation (<i
lang="en">quotation</i>)</a></h3>
<p>
Avec tous ces caractères spéciaux, comment faire pour passer des
arguments bizarres à une commande&nbsp;? Par exemple, comment faire
afficher un point d'interrogation suivi d'une étoile et d'un dollar par
<code>echo</code>&nbsp;?</p>
<p>
On ne peut pas les taper directement dans la ligne de commande (ici,
avec le shell <code>zsh</code>), car le shell essaye de les interpréter
comme des jokers&nbsp;:
</p>
<pre><span class="prompt">chaland ~</span> echo ?*$
zsh: no matches found: ?*$</pre>
<p> Il faut donc indiquer au shell de ne pas interpréter ces caractères
comme des jokers. Pour cela, nous allons utiliser des mécanismes fournis
par le shell&nbsp;: les <i lang="en">quotations</i> (mot anglais
signifiant <em>citation</em>).
</p>
<p>
Les mécanismes de citation sont les suivants&nbsp;:
</p>
<ul>
<li> le backslash (<code>\</code>), qui protège un caractère de
l'interprétation par le shell&nbsp;;</li>
<li> les apostrophes ou <i lang="en">simple quotes</i>
(<code>'</code>), qui protègent une chaîne de caractères de
l'interprétation par le shell&nbsp;;</li>
<li> les guillemets doubles ou <i lang="en">double quotes</i>
(<code>&quot;</code>), qui protègent également une chaîne de caractères
de l'interprétation par le shell, mais avec plus de souplesse que les
précédents&nbsp;;</li>
<li> les <i lang="en">backquotes</i> (<code>`</code>), qui cite la
sortie d'une commande.</li>
</ul>
<h4>Le backslash (<code>\</code>)</h4>
<p>
Il suffit de précéder un caractère spécial d'un backslash, et le shell
remplace ces deux caractères par le caractère spécial seul. Évidemment, le
backslash est lui-même un caractère spécial.
</p>
<p>
Exemples&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> echo \?\*\$
?*$
<span class="prompt">chaland ~ $</span> echo \\\?\\\*\\\$
\?\*\$</pre>
<h4>Les apostrophes ou simples quotes (<code>'</code>)</h4>
<p>
Un autre moyen est d'inclure une chaîne de caractères entre apostrophes
(simples quotes) <code>'</code>. Tout ce qui se trouve entre deux
apostrophes sera passé tel quel par le shell à la
commande. Exemple&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> echo '$?*'
$?*</pre>
<h4>Les guillemets doubles ou doubles quotes (<code>&quot;</code>)</h4>
<p>
Les guillemets se comportent comme les apostrophes, à une exception près: les
dollars et les backslashes sont interprétés entre les guillemets.
Exemple&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> echo "$HOME/*"
/users/87/maths/doligez/*</pre>
<p>
Une technique utile: Quand on juxtapose deux chaînes de caractères
quotées, le shell les concatène, et elles ne forment qu'un argument.
Exemple&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> echo "'"'"'
'"</pre>
<p>
Quant aux interactions plus compliquées (backslashes à l'intérieur des
guillemets, guillemets à l'intérieur des apostrophes, etc.), le meilleur moyen
de savoir si ça donne bien le résultat attendu est d'essayer. La commande
<code>echo</code> est bien utile dans ce cas.
</p>
<h4>Les backquotes (<code>`</code>)</h4>
<p>
Dernière forme de citation&nbsp;: <code>`<em>commande</em>`</code>. Le
shell exécute la <em>commande</em> indiquée entre backquotes, lit la
sortie de la commande mot par mot, et remplace <code>`</code>
<em>commande</em> <code>`</code> par la liste de ces mots.
Exemple&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> echo `ls`
Mail News bin foo lib misc mur notes.aux notes.dvi notes.log
notes.tex planar text
<span class="prompt">chaland ~ $</span> ls -l `which emacs`
-rwxr-xr-t 1 root taff 978944 Jul 16 1991 /usr/local/bin/emacs</pre>
<p>
La commande <code>which <em>cmd</em></code> employée ci-dessus affiche sur sa
sortie le nom absolu du fichier exécuté par le shell quand on lance la
commande it <em>cmd</em> :
</p>
<pre>
<span class="prompt">chaland ~ $</span> which emacs
/usr/local/bin/emacs</pre>
<p>Vous êtes maintenant en mesure de faire ces <a
href="&url.tuteurs;unix/exercices/">exercices</a> pour vous
entraîner.</p>
<div class="metainformation">
Basé sur un polycopié de Roberto Di Cosmo, Xavier Leroy et Damien Doligez.
Modifications&nbsp;: Nicolas George, Baptiste Mélès.
Dernière modification le <date value="$Date: 2005-05-31 11:33:37 $" />.
</div>
</body>
</html>

392
unix/shell/entreesortie.tml Normal file
View file

@ -0,0 +1,392 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
"tuteurs://DTD/tml.dtd">
<html>
<head>
<title>Entrée/sortie</title>
</head>
<body>
<h1>Entrée, sortie, redirection</h1>
<h2><a name="redirections">Entrée et sortie</a></h2>
<h3>Que sont l'entrée et la sortie d'une commande&nbsp;?</h3>
<p>
Un programme consiste à traiter des données, et à renvoyer des données
transformées&nbsp;: il transforme des informations, et c'est pourquoi
l'on parle d'<em>informatique</em>.</p>
<p> Prenons le programme <code>hachoir</code>, par exemple&nbsp;: on y
fait entrer des choses, elles sortent sous une autre forme, et dans
l'intervalle, elles subissent des transformations régulières. Par
exemple, on fait entrer une vache, il en ressort du steak haché&nbsp;;
on y fait entrer des carottes, il en ressort des carottes rapées.
</p>
<p>
Deux concepts permettent de modéliser cette transformation
d'informations&nbsp;: les concepts d'entrée et de
sortie. L'<strong>entrée</strong>, c'est la vache ou les carottes&nbsp;;
la <strong>sortie</strong>, c'est le steak haché ou les carottes
rapées.
</p>
<h3>Sortie standard et sortie d'erreur</h3>
<p>
Mais cette première distinction entre entrée et sortie ne suffit pas,
car la sortie d'un programme, c'est-à-dire les messages qu'il renvoie,
sont de deux ordres&nbsp;: il y a les <em>messages normaux</em> relevant
de la transformation d'informations (par exemple le steak haché ou les
carottes rapées), mais il y a aussi des <em>messages d'erreur</em>.</p>
<p> Par exemple, si vous mettez, sur le tapis roulant qui mène au
hachoir, un objet trop gros pour y rentrer, le hachoir, s'il est bien
conçu, devra vous prévenir qu'il n'arrive pas à hacher un objet si
gros. Et ce message, quoiqu'il sorte aussi bien du hachoir que le steak
haché, ne doit pas être traité de la même façon à sa sortie, et n'est
pas suivi des mêmes conséquences. C'est pourquoi l'on distingue la
<strong>sortie standard</strong> et la <strong>sortie d'erreur</strong>.
</p>
<h3>Comportement par défaut</h3>
<p>
Pour résumer, chaque commande a donc&nbsp;:</p>
<ul>
<li> une <em>entrée standard</em>, </li>
<li> une <em>sortie standard</em> et </li>
<li> une <em>sortie d'erreur</em>.</li>
</ul>
<p> Par défaut, l'entrée standard est le clavier, la sortie standard est
l'écran, et la sortie d'erreur est aussi l'écran.</p>
<p> C'est sur le clavier que vous tapez&nbsp;; ce que vous tapez et ce
que renvoient les programmes s'inscrit à l'écran&nbsp;; les messages
d'erreur renvoyés par les programmes s'affichent à l'écran.
</p>
<h2>Les redirections</h2>
<p>
Mais il ne s'agit là que du comportement par défaut, et pas d'un
comportement obligatoire. Vous pouvez tout à fait orienter différemment
vos programmes.</p>
<p> Par exemple, si vous donnez une vache comme entrée au hachoir, vous
pouvez orienter la sortie vers une imprimante (au lieu de l'écran,
proposé par défaut), et vous imprimerez ainsi du steak haché. </p>
<p>
Ou encore, vous pouvez donner un plant de carottes comme entrée au
programme <code>cueillette</code>, et envoyer la sortie (c'est-à-dire
les carottes cueillies) au programme <code>hachoir</code>.
</p>
<p>
Nous allons étudier successivement&nbsp;:
</p>
<ul>
<li> comment rediriger la sortie d'une commande vers un
fichier&nbsp;;</li>
<li> comment ajouter la sortie d'une commande à la fin d'un
fichier&nbsp;;</li>
<li> comment utiliser un fichier comme entrée d'une commande&nbsp;;</li>
<li> comment utiliser la sortie d'une commande comme entrée d'une
autre.</li>
</ul>
<p>
Ces différentes configurations esquissent la grammaire d'une utilisation
très performante des fonctionnalités du shell&nbsp;; <strong>vous saurez
en effet comment brancher un fichier sur l'entrée ou sur la sortie d'une
commande, et comment brancher la sortie d'une commande sur l'entrée
d'une autre</strong>. Il n'y a pas d'autre combinaison possible.
</p>
<h3>Rediriger la sortie dans un fichier : <code>&gt;</code></h3>
<p>
On peut <em> rediriger</em> la sortie standard d'une commande vers un
fichier (caractère «&nbsp;<code>&gt;</code>&nbsp;»). Le résultat de la
commande sera placé dans le fichier au lieu de s'afficher sur l'écran.
Exemple&nbsp;:
</p>
<pre><span class="prompt">chaland ~ $</span> ls -l &gt; foo</pre>
<p>
Le résultat de <code>ls -l</code> ne s'affiche pas à l'écran, mais il est placé
dans le fichier <code>foo</code>. On peut alors taper
</p>
<pre><span class="prompt">chaland ~ $</span> less foo</pre>
<p class="continue">
(ou <code>more foo</code>)
pour lire le fichier page par page.
</p>
<h3>Ajouter la sortie à un fichier : <code>&gt;&gt;</code></h3>
<p> On veut parfois ajouter la sortie d'un programme à un fichier, sans
effacer ce qui précède. Or, par défaut, si l'on tape plusieurs fois
</p>
<pre><span class="prompt">chaland ~ $</span> ls -l &gt; foo</pre>
<p class="continue">
à chaque fois, le contenu antérieur du fichier <code>foo</code> est
écrasé par le contenu ultérieur.
</p>
<p>
Pour éviter cela, il existe l'outil de redirection &gt;&gt;. Ainsi, si
vous tapez plusieurs fois
</p>
<pre><span class="prompt">chaland ~ $</span> ls -l &gt;&gt; foo</pre>
<p class="continue">
le fichier <code>foo</code> contiendra <em>à la suite</em> tout ce que
vous a renvoyé la commande.
</p>
<h3>Rediriger l'entrée : <code>&lt;</code></h3>
<p>
On peut aussi rediriger l'entrée standard d'une commande (caractère
«&nbsp;<code>&lt;</code>&nbsp;»). La commande lira alors le fichier au
lieu du clavier. Exemple&nbsp;:
</p>
<pre><span class="prompt">chaland ~ $</span> elm leroy &lt; foo</pre>
<p class="continue">
envoie par mail à Xavier Leroy le résultat de la commande <code>ls -l</code> de
tout à l'heure.
</p>
<p>
On peut aussi taper <code>more &lt; foo</code> qui est équivalent à
<code>more foo</code> car <code>more</code> sans argument lit son entrée
standard et l'affiche page par page sur le terminal.
</p>
<h3>Connecter la sortie d'une commande sur l'entrée d'une autre :
<code>|</code></h3>
<p>
Il devient rapidement ennuyeux de taper&nbsp;:
</p>
<pre><span class="prompt">chaland ~ $</span> ls -l &gt; foo
<span class="prompt">chaland ~ $</span> less &lt; foo
<span class="prompt">chaland ~ $</span> rm foo</pre>
<p>
On peut se passer du fichier intermédiaire (<code>foo</code> dans notre
exemple) grâce à un <em>pipe</em> (caractère
«&nbsp;<code>|</code>&nbsp;»). Un pipe connecte directement la sortie
standard d'une commande sur l'entrée standard d'une autre commande.
Exemple&nbsp;: pour afficher page par page la liste des fichiers du
répertoire courant, faire
</p>
<pre><span class="prompt">chaland ~ $</span> ls -l | less</pre>
<p>
Le pipe, ou tube, est d'un usage très courant, et rend beaucoup de
services.
</p>
<h2>Succès et erreur</h2>
<p>
On a parfois besoin de savoir si une commande a réussi ou non avant d'en
lancer une autre. Les indicateurs <code>&amp;&amp;</code> et
<code>||</code> permettent, respectivement, de lancer une commande si
(et seulement si) la précédente a réussi ou échoué.
</p>
<p>
Par exemple, si j'ai un fichier <code>foo</code>, j'obtiens&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> ls foo &amp;&amp; echo "J'ai un fichier foo."
foo
J'ai un fichier foo.
</pre>
<p>
Si je n'ai pas de fichier <code>foo</code>, le message ne s'affiche
pas. En revanche, si je tape alors&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> ls foo || echo "Je n'ai pas de fichier foo."
ls: foo: No such file or directory
Je n'ai pas de fichier foo.
</pre>
<h2>Redirection des sorties standard et d'erreur</h2>
<p>
Il est parfois utile de rediriger la sortie standard et la sortie
d'erreur vers un même endroit. Pour cela, on utilise le motif
<code>2&gt;&amp;1</code> avant la redirection.
</p>
<table class="tableau">
<tr>
<th> Fonction </th>
<th> Bourne shell (sh, bash) </th>
<th> Z-shell (zsh) </th>
</tr>
<tr>
<td>Redirige la sortie d'erreur (2) et la sortie standard (1) sur l'entrée
de la commande suivante</td>
<td><code>2&gt;&amp;1 |</code></td>
<td><code> |&amp;</code></td>
</tr>
<tr>
<td>Redirige la sortie d'erreur et la sortie standard dans un fichier</td>
<td><code>2&gt;&amp;1 &gt;</code></td>
<td><code> &gt;&amp;</code></td>
</tr>
<tr>
<td>Redirige la sortie d'erreur et la sortie
standard à la fin d'un fichier existant</td>
<td><code>2&gt;&amp;1 &gt;&gt;</code></td>
<td><code>&gt;&gt;&amp;</code></td>
</tr>
</table>
<h3>Remarques</h3>
<p>
Normalement, une redirection avec <code>&gt;</code> sur un fichier qui
existe déjà efface le contenu du fichier avant d'y placer le résultat de la
commande. Les shells ont des options pour demander confirmation, ou
refuser d'effacer le fichier.
</p>
<p>
Une ligne de commandes contenant des <code>|</code> s'appelle un pipe-line.
Quelques commandes souvent utilisées dans les pipe-lines sont:
</p>
<ul>
<li><a href="fichiers.html#less">more</a> (ou <code>less</code>,
bien plus évolué) à la fin du pipe-line, affiche le résultat page par page,
pour laisser le temps de le lire.
</li>
<li><a href="divers.html#wc">wc</a> compte le nombre de
caractères, de mots et de lignes de son entrée.
</li>
<li><a href="chercher.html#grep">grep</a> cherche dans son
entre les lignes contenant un mot donné, et les écrit sur sa sortie.
</li>
<li><a href="&url.tuteurs;unix/exercices/enchainer.html#syst">sort</a> lit toutes les lignes
de son entrée, les trie, et les écrit dans l'ordre sur sa sortie. Par défaut
l'ordre est alphabétique.
</li>
<li><a href="fichiers.html#head">tail</a> écrit sur sa sortie
les dernières lignes de son entrée.
</li>
<li><a href="fichiers.html#head">head</a> écrit sur sa sortie
les premières lignes de son entrée.
</li>
<li><a href="fichiers.html#cat">cat</a> copie plusieurs fichiers
sur sa sortie.
</li>
<li><code>fold</code> coupe les lignes de son
entrée à 80 caractères et écrit le résultat sur sa sortie.
</li>
</ul>
<h3>Exemples</h3>
<pre>cat glop buz &gt; toto</pre>
<p>
Concatène les fichiers <code>glop</code> et <code>buz</code> et place le
résultat dans <code>toto</code>.
</p>
<pre>wc -w /usr/dict/words</pre>
<p>
Affiche le nombre de mots du dictionnaire Unix.
</p>
<pre>grep gag /usr/dict/words | tail</pre>
<p>
Affiche les 10 derniers mots du dictionnaire qui contiennent la chaîne
<code>gag</code>.
</p>
<h2>Récapitulatif</h2>
<p>
La panoplie complète des redirections est la suivante&nbsp;:
</p>
<ul>
<li><code>&gt;</code> : change la sortie standard de la commande pour la placer
dans un fichier.
</li>
<li><code>&lt;</code> : change l'entrée standard de la commande pour la prendre
dans un fichier.
</li>
<li><code>|</code> : branche la sortie standard de la commande de gauche sur
l'entrée standard de la commande de droite.
</li>
<li><code>&gt;&gt;</code> : change la sortie standard pour l'ajouter à la fin
d'un fichier existant.
</li>
<li><code>||</code> : exécuter la commande suivante si la première a
échoué.
</li>
<li><code>&amp;&amp;</code> : n'exécuter la commande suivante que si la
première a réussi.
</li>
</ul>
<p>Vous êtes maintenant en mesure de faire ces <a
href="&url.tuteurs;unix/exercices/">exercices</a> pour vous
entraîner.</p>
<div class="metainformation">
Basé sur un polycopié de Roberto Di Cosmo, Xavier Leroy et Damien Doligez.
Modifications&nbsp;: Nicolas George, Baptiste Mélès.
Dernière modification le <date value="$Date: 2005-05-31 11:33:38 $" />.
</div>
</body>
</html>

220
unix/shell/index.tml Normal file
View file

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
"tuteurs://DTD/tml.dtd">
<html>
<head>
<title>Shell</title>
</head>
<body>
<h1>Présentation du shell</h1>
<p>
Le mot <em>shell</em> signifie «&nbsp;coquille&nbsp;» en anglais. Il
s'agit du programme que vous utilisez régulièrement sur les machines
Unix de l'ENS (que ce soient les Sun, les PC sous FreeBSD ou autres
dérivés d'Unix), et qui interprète les commandes. Par exemple, vous y
tapez <code>pine</code> ou <code>mutt</code>, <code>forum</code>,
<code>cc</code>, <code>mozilla</code>, etc.
</p>
<p>
Mais quel rapport avec une coquille&nbsp;? Eh bien, dans une coquille
vous pouvez mettre de l'eau, pour la porter ensuite à votre
bouche&nbsp;; vous pouvez y mettre du sable avant de le verser dans des
bocaux&nbsp;; en somme, une coquille est un récipient qui permet
de manipuler toutes sortes de contenus. Il en va de même du shell. C'est
un outil en mode texte qui permet l'exploitation d'un grand nombre de
ressources de l'ordinateur.
</p>
<p>
Cette page vous donnera les rudiments pour exploiter les deux
principales fonctionnalités du shell&nbsp;:
</p>
<ul>
<li> l'usage interactif, reposant sur les <strong>lignes de
commandes</strong>&nbsp;;</li>
<li> la conception de <strong>scripts</strong> (programmes écrits en
shell). </li>
</ul>
<h2><a name="etats">Les deux états du shell</a>
</h2>
<p>
Le shell, comme le normalien, ne connaît que deux états&nbsp;:
</p>
<ul>
<li> le travail&nbsp;;</li>
<li> l'inactivité.</li>
</ul>
<p>
Le shell, une fois lancé, est inactif&nbsp;: il attend qu'on lui donne
des ordres. Quand on lui en donne un, il l'exécute&nbsp;; et quand il a
terminé, il retourne à son état d'inactivité, en attente d'un nouveau
commandement.
</p>
<p>
Quand le shell est inactif, il affiche une <em>invite</em>
(<em>prompt</em> en anglais), qui ressemble à cela&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span>
</pre>
<p>
Un curseur, parfois clignotant, indique que le shell attend que vous lui
tapiez des instructions.
</p>
<h2><a name="path">Comment le shell trouve-t-il les commandes ?</a>
</h2>
<h3>L'ordre de recherche</h3>
<p>
J'ai l'habitude de taper des commandes dans le shell, et je vois qu'il
réagit. Mais comment comprend-il ce que je veux faire&nbsp;?
</p>
<p>
Prenons un cas simple. Je tape la commande <code>bonjour</code> à
l'invite (<em>prompt</em>) du shell. Il va chercher à plusieurs endroits
ce que j'entends par là&nbsp;:
</p>
<ol>
<li> d'abord, il va se demander si <code>bonjour</code> n'est pas une de
ses commandes intégrées&nbsp;; si c'est le cas, il l'exécute
directement, sinon il passe à l'étape suivante&nbsp;;</li>
<li> ensuite, il va lire le contenu d'une variable, qui s'appelle
<code>PATH</code>, et qui indique le «&nbsp;chemin&nbsp;» où trouver les
commandes que l'on appelle. Par exemple, si la variable PATH contient
les répertoires&nbsp;:
<ul>
<li> <code>/usr/bin</code> </li>
<li> <code>/bin</code> et </li>
<li> <code>/home/toto/bin</code>,</li>
</ul>
alors le shell va chercher successivement les commandes&nbsp;:
<ul>
<li> <code>/usr/bin/bonjour</code>,</li>
<li> <code>/bin/bonjour</code> et </li>
<li> <code>/home/toto/bin/bonjour</code>&nbsp;;</li>
</ul></li>
<li> enfin, s'il ne trouve la commande dans aucun des répertoires
référencés par le <code>PATH</code>, il va renvoyer un message d'erreur
en disant que désolé, il ne voit pas ce que l'on entend par
<code>bonjour</code>. Exemple&nbsp;:
<pre>
<span class="prompt">chaland ~ $</span> bonjour
bonjour: Command not found</pre> </li>
</ol>
<p> La <a href="#variables">variable</a> <code>PATH</code> consiste en
une liste de répertoires séparés par des
«&nbsp;<code>:</code>&nbsp;». Si vous voulez voir à quoi ressemble votre
PATH, tapez&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> echo $PATH</pre>
<h3><a name="builtins">Les commandes internes</a></h3>
<p>
Certaines commandes du shell ne sont pas des programmes mais des
commandes <em>internes</em> (<em>builtins functions</em>). Comme nous
l'avons vu, elles sont directement reconnues et exécutées par le shell.
Un exemple de commande interne est <code>cd</code>&nbsp;; elle modifie
le répertoire courant du shell.
</p>
<div class="attention">
<p>
Attention&nbsp;: si vous créez un script (c'est-à-dire un programme
écrit en langage shell) qui utilise <code>cd</code>, il ne modifie pas
le répertoire courant du shell qui lance ce script, mais celui d'un
shell qui est créé à l'occasion de l'exécution de ce script, et qui
meurt à la fin de cette exécution.</p>
<p> Exemple&nbsp;: je crée un script <code>aller</code> qui contient les
lignes suivantes&nbsp;: </p>
<pre>
#! /bin/sh
cd $*</pre>
<p>
Nous aurons alors&nbsp;:
</p>
<pre>
<span class="prompt">chaland ~ $</span> aller toto
<span class="prompt">chaland ~ $</span> cd toto
<span class="prompt">chaland ~/toto $</span></pre>
</div>
<h2><a name="prog">Quels programmes utilisent le langage du shell ?</a></h2>
<h3>Les scripts shell</h3>
<p>
La répétition de commandes complexes en ligne de commande du shell est
rapidement fastidieuse&nbsp;; aussi est-il très pratique de connaître
les bases de la programmation de scripts shell. Les scripts servent à
automatiser ou systématiser des tâches.
</p>
<h3>Le script <code>.profile</code></h3>
<p>
Il existe un script spécial, qui est exécuté au moment où on se connecte. Ce
script est contenu dans le fichier <code>$HOME/.profile</code>. C'est ce
fichier qui vous dit s'il y a de nouveaux messages dans forum, si vous avez
du courrier, etc.
</p>
<p>
Ce fichier est normalement mis à jour automatiquement par les scripts de la
config conscrits. Il est néanmoins possible de le modifier pour changer des
options.
</p>
<h3>Le script <code>.xinitrc</code></h3>
<p> Il existe encore le script <code>.xinitrc</code>, qui lance X&nbsp;;
X&nbsp;est le gestionnaire de fenêtres classique sous Unix.
</p>
<h3>Créer ses propres scripts</h3>
<p>
Le nombre de scripts possibles est illimité&nbsp;; vous pouvez en créer
autant que vous voulez, selon vos besoins&nbsp;: c'est ainsi que l'on
personnalise son système et qu'on l'adapte à ses exigences, plutôt que
l'inverse.
</p>
<div class="metainformation">
Basé sur un polycopié de Roberto Di Cosmo, Xavier Leroy et Damien
Doligez.
Modifications&nbsp;: Nicolas George, Baptiste Mélès.
Dernière modification le <date value="$Date: 2005-05-31 11:33:38 $"
/>.
</div>
</body>
</html>

452
unix/shell/script.tml Normal file
View file

@ -0,0 +1,452 @@
<?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>, à l'exception de
<code>setenv</code> et de certaines redirections signalées. </p>
<h2>Créer un script</h2>
<h3>Rendre un script exécutable</h3>
<p> Pour être un script, un fichier 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="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="index.html">page principale sur le 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 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.
</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><a name="structures">Structures de contrôle</a></h2>
<p>
C'est avec les structures de contrôle qu'un programme peut commencer à
devenir sérieux. Le principe général de ces structures est le
suivant&nbsp;: adapter le comportement du programme selon les réponses
apportées à certaines questions.
</p>
<p>
Nous avons déjà vu une application possible des structures de contrôle
en parlant des variables. Le programme <code>hachoir</code> conçu par
Robert le jardinier pose la question suivante&nbsp;: est-ce que
<code>$USER</code> vaut «&nbsp;Robert&nbsp;»&nbsp;?
</p>
<ul>
<li> si oui, alors le hachoir doit se mettre en marche&nbsp;;</li>
<li> si non, alors le hachoir doit refuser de se mettre en
marche.</li>
</ul>
<h3>La boucle <code>if</code></h3>
<p>
La structure de contrôle <code>if</code> est la plus courante de toutes,
et la plus élémentaire. Elle est constituée de quatre termes&nbsp;:</p>
<ol>
<li> <code>if</code> (si), qui marque la condition à remplir&nbsp;;
</li>
<li> <code>then</code> (alors), qui marque les conséquences si la
condition est remplie&nbsp;; </li>
<li> <code>else</code> (sinon), qui est facultatif et marque le
comportement à adopter si la condition n'est pas remplie&nbsp;; </li>
<li> <code>fi</code> (<code>if</code> à l'envers), qui marque la fin de
la boucle.</li>
</ol>
<pre>if <em>commande</em> ; then <em>commandes</em> ; else <em>commandes</em> ; fi</pre>
<p class="continue"> ou bien (car le point et virgule équivaut à un saut
de ligne)&nbsp;:</p>
<pre>if <em>commande</em>
then <em>commandes</em>
else <em>commandes</em>
fi</pre>
<p>
Par exemple, pour Robert le jardinier, on aura&nbsp;:
</p>
<pre>if test $USER = Robert
then hacher_menu_comme_chair_à_pâté $*
else echo "Quand tu seras grand, $USER."
echo "Et fais bien attention en traversant la rue."
fi</pre>
<h3>La boucle <code>for</code></h3>
<p>
La boucle <code>for</code> affecte successivement une variable chaque
chaîne de caractères trouvée dans une <em>liste de chaînes</em>, et
exécute les <em>commandes</em> une fois pour chaque chaîne.
</p>
<pre>for <em>var</em> in <em>liste de chaînes</em> ; do <em>commandes</em> ; done</pre>
<p class="continue"> ou bien&nbsp;:</p>
<pre>for <em>var</em> in <em>liste de chaînes</em>
do <em>commandes</em>
done</pre>
<p>
Rappel : <code>$<em>var</em></code> accède à la valeur courante de
<em>var</em>. La partie <em>commandes</em> est une suite de commandes,
séparées par des points et virgules (<code>;</code>) ou des retours à la
ligne.
</p>
<p>
Exemple&nbsp;:
</p>
<pre>for i in *; do echo &quot;$i&quot;; done</pre>
<p class="continue">
affiche les noms de tous les fichiers du répertoire courant, un par ligne.
</p>
<p>
Remarque&nbsp;: plus un programme grossit, plus il importe que son
contenu soit lisible. <strong>Les noms de variable doivent donc être le
plus lisibles possible</strong>, pour permettre à d'autres gens, ou bien
à vous-même dans quelques mois, de comprendre rapidement votre script.
Ainsi, il peut être plus explicite d'écrire, au lieu de «&nbsp;<code>for
i in *; do echo &quot;$i&quot;; done</code>&nbsp;»&nbsp;:
</p>
<pre>for fichier in *; do echo &quot;$fichier&quot;; done</pre>
<p>
En outre, pour des raisons de lisibilité, <strong>n'hésitez pas à
gaspiller des lignes de code</strong> en sautant des lignes&nbsp;; les
lignes de code ne sont pas une ressource rare&nbsp;:
</p>
<pre>for fichier in *
do echo &quot;$fichier&quot;
done</pre>
<h3>La boucle <code>while</code></h3>
<pre>while <em>commande</em>
do <em>commande</em>
done</pre>
<p>
La boucle <code>while</code> exécute les <em>commandes</em> de manière
répétée tant que la première <em>commande</em> réussit.</p>
<p> Par exemple&nbsp;: <em>tant que</em> le mot de passe correct n'a pas
été donné, refuser l'accès et redemander le mot de passe.
</p>
<h3>La boucle <code>case</code></h3>
<pre>case <em>chaîne</em> in
<em>pattern</em>) <em>commande</em> ;;
<em>pattern</em>) <em>commande</em> ;;
esac</pre>
<p>
La boucle <code>case</code> exécute la première <em>commande</em> telle
que la <em>chaîne</em> est de la forme <em>pattern</em>. Un
<em>pattern</em> (motif) est un mot contenant éventuellement les
constructions <code>*</code>, <code>?</code>, <code>[a-d]</code>, avec
la même signification que pour les raccourcis dans les noms de
fichiers. Exemple&nbsp;:
</p>
<pre>
case $var in
[0-9]*) echo 'Nombre';;
[a-zA-Z]*) echo 'Mot';;
*) echo 'Autre chose';;
esac</pre>
<p>
Vous observerez que l'on clôt la boucle <code>case</code> avec
<code>esac</code>, qui est le mot <code>case</code> à l'envers, de même
que l'on clôt <code>if</code> avec <code>fi</code>.
</p>
<h2><a name="retour">Code de retour</a></h2>
<p>
On remarque que la condition des commandes <code>if</code> et
<code>while</code> est une commande. Chaque commande renvoie un code de
retour (qui est ignoré en utilisation normale). Si le code est 0, la
commande a réussi&nbsp;; sinon, la commande a échoué. Par exemple, le
compilateur <code>cc</code> renvoie un code d'erreur non nul si le
fichier compilé contient des erreurs, ou s'il n'existe pas.
</p>
<p>
Les commandes <code>if</code> et <code>while</code> considèrent donc le
code de retour 0 comme «&nbsp;vrai&nbsp;», et tout autre code comme
«&nbsp;faux&nbsp;».
</p>
<p>
Il existe une commande <code>test</code>, qui évalue des expressions
booléennes (c'est-à-dire dont la valeur ne peut être que vraie ou
fausse, 1 ou 0) passées en argument, et renvoie un code de retour en
fonction du résultat. Elle est bien utile pour les scripts.
Exemple&nbsp;:
</p>
<pre>
if test $var = foo
then echo 'La variable vaut foo'
else echo 'La variable ne vaut pas foo'
fi</pre>
<p>Vous êtes maintenant en mesure de faire ces <a
href="&url.tuteurs;unix/exercices/">exercices</a> pour vous
entraîner.</p>
<div class="metainformation">
Basé sur un polycopié de Roberto Di Cosmo, Xavier Leroy et Damien Doligez.
Modifications&nbsp;: Nicolas George, Baptiste Mélès.
Dernière modification le <date value="$Date: 2005-05-31 11:33:38 $" />.
</div>
</body>
</html>

225
unix/shell/variable.tml Normal file
View file

@ -0,0 +1,225 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
"tuteurs://DTD/tml.dtd">
<html>
<head>
<title>Variables</title>
</head>
<body>
<h1><a name="variables">Les variables en shell</a></h1>
<h2>Qu'est-ce qu'une variable&nbsp;?</h2>
<p>
Une variable est l'assignation d'une étiquette à un contenu&nbsp;; ce
contenu, comme l'indique le mot «&nbsp;variable&nbsp;», peut changer
autant que l'on veut&nbsp;; l'assignation de l'étiquette à ce contenu,
elle, est fixe, aussi longtemps que l'on ne dissout pas la
variable. </p>
<p>
La notion de variable est commune à presque tous les langages
informatiques, et en particulier aux langages de programmation. Ceux qui
ont déjà manipulé des langages sont donc familiers avec cette
notion. Pour les autres, un petit exemple les aidera peut-être à la
saisir.
</p>
<p>
Mettons que vous programmiez un hachoir, comme nous le proposions sur la
page consacrée au <a href="commande.html">shell en ligne de
commande</a>. Un hachoir est un instrument dangereux, à ne pas mettre
à la portée des enfants. Robert le jardinier, qui a conçu ce
hachoir, veut être le seul à pouvoir l'utiliser, sans quoi le
petit Émile pourrait se blesser en y mettant le doigt.</p>
<p> Ainsi, il va dire au programme <code>hachoir</code> de vérifier la
variable <code>USER</code>, qui contient le nom de l'utilisateur. Si le
nom «&nbsp;Robert&nbsp;» est associé à l'étiquette <code>USER</code>,
alors le programme se met en route&nbsp;; sinon, il dit à l'utilisateur
de ne pas utiliser cet instrument sans la présence d'un adulte, et de
bien regarder des deux côtés avant de traverser la rue.
</p>
<p>
Certaines variables sont prédéfinies, par exemple
<code>USER</code>&nbsp;; mais on peut en créer autant que l'on veut. Par
exemple, si Robert veut autoriser d'autres adultes que lui à utiliser
son hachoir, il peut faire que le programme demande à l'utilisateur quel
âge il a&nbsp;; la réponse est enregistrée dans la variable
<code>age</code>&nbsp;; ensuite, le programme va examiner le contenu de
cette variable. Si <code>age &gt;= 18</code>, alors le hachoir peut se
mettre en route&nbsp;; mais si <code>age &lt; 18</code>, le hachoir
refuse de se mettre en marche.
</p>
<h2>Les variables en shell</h2>
<p>
En shell, pour désigner le contenu d'une variable, on écrit le nom de la
variable précédé du signe dollar. Exemple&nbsp;: <code>echo
$HOME</code> affiche le nom du répertoire personnel de l'utilisateur,
mémorisé par la variable <code>HOME</code>.
</p>
<h3>Les noms de variables</h3>
<p>
Par convention, les variables relevant du système, comme
<code>HOME</code>, <code>USER</code> et beaucoup d'autres, sont en
majuscules, tandis que l'on recommande d'écrire en minuscules les
variables que l'on se crée soi-même. On évite ainsi la désagréable
surprise de remplacer une variable importante et de casser tout ou
partie de son système.
</p>
<p>
Les noms de variables sont en effet sensibles à la casse&nbsp;:
<code>USER</code>, <code>user</code>, <code>User</code>,
<code>uSeR</code> etc. sont des variables différentes.
</p>
<h3>Définir une variable</h3>
<p>
La façon de donner une valeur à une variable varie selon le type de shell
utilisé :
</p>
<p>
<strong>C-Shell</strong> (<code>csh</code>, <code>tcsh</code>,
<code>lcsh</code>) : on utilise la commande <code>setenv</code> :
</p>
<pre>
<span class="prompt">chaland ~ $</span> setenv foo bar
<span class="prompt">chaland ~ $</span> echo $foo
bar</pre>
<p>
Famille des <strong>Bourne Shell</strong> (<code>sh</code>, <code>bash</code>,
<code>zsh</code>, <code>ksh</code>) : on utilise <code>export</code> :
</p>
<pre>
<span class="prompt">chaland ~ $</span> foo=bar
<span class="prompt">chaland ~ $</span> export foo
<span class="prompt">chaland ~ $</span> echo $foo
bar</pre>
<div class="attention">
Comme vous pouvez le remarquer&nbsp;:
<ul>
<li> pour <strong>définir</strong> le contenu d'une variable, on
écrit simplement son nom, <strong>sans le
signe&nbsp;$</strong>&nbsp;;</li>
<li> tandis que pour <strong>utiliser</strong> le contenu d'une
variable, on fait précéder son nom du
<strong>signe&nbsp;$</strong>. </li>
</ul>
</div>
<h3>Les variables d'environnement</h3>
<p>
Les valeurs des variables sont accessibles aux commandes lancées par le
shell. L'ensemble de ces valeurs constitue l'<em>environnement</em>.
On peut aussi supprimer une variable de l'environnement avec
<code>unsetenv</code> (C-Shell) ou <code>unset</code> (Bourne Shell).
</p>
<p>
Quelques variables d'environnement:
</p>
<ul>
<li><code>DISPLAY</code> : L'écran sur lequel les programmes&nbsp;X
travaillent. Cette variable est souvent de la forme&nbsp;:
<code>machine.somehost.somewhere:0.0</code> Si cette variable est vide,
c'est qu'il n'y a pas d'affichage graphique possible. </li>
<li><code>PRINTER</code> : pour les commandes d'impression. Contient le
nom de l'imprimante sur laquelle il faut envoyer vos fichiers. </li>
<li><code>EDITOR</code> : utilisée par <code>mutt</code>,
<code>forum</code>, et beaucoup d'autres commandes. Contient le nom de
votre éditeur de textes préféré. </li>
<li><code>VISUAL</code> : la même chose qu'<code>EDITOR</code>. </li>
<li><code>SHELL</code> : contient le nom de votre shell. </li>
<li><code>HOME</code> : contient le nom de votre répertoire personnel.
</li>
<li><code>USER</code> : contient votre nom de login. </li>
<li><code>LOGNAME</code> : la même chose que <code>USER</code>. </li>
<li><code>PATH</code> : contient une liste de répertoires dans lesquels
le shell va chercher les commandes.
</li>
</ul>
<p>
Exercice : Assurez-vous que <code>/usr/local/games/bin</code> se trouve
bien dans votre PATH.
</p>
<h2><a name="shvariables">Utiliser les variables dans un script</a></h2>
<p>
Dans les scripts, on peut utiliser des variables définies à l'extérieur
(avec <code>setenv</code> ou <code>export</code>), mais aussi définir ses
variables locales propres au script. On donne une valeur à une variable avec
une commande de la forme
<code><em>nom-de-variable</em>=<em>valeur</em></code>. Les variables sont
utilisées pour stocker des informations.
</p>
<p>
On a aussi des variables spéciales, initialisées automatiquement au
début du script:
</p>
<table class="tableau">
<tr>
<td><code>$0</code></td>
<td>Le nom de la commande (i.e. : du script)</td>
</tr>
<tr>
<td><code>$1</code>, <code>$2</code>, etc.</td>
<td>Le premier, deuxième, etc, argument passés au script.</td>
</tr>
<tr>
<td><code>$*</code></td>
<td>La liste de tous les arguments passés au script.</td>
</tr>
<tr>
<td><code>$#</code></td>
<td>Le nombre d'arguments passés au script.</td>
</tr>
<tr>
<td><code>$?</code></td>
<td>Le code de retour de la dernière commande lancée.</td>
</tr>
<tr>
<td><code>$!</code> </td>
<td>Le numéro de process de la dernière commande lancée en tâche de fond.</td>
</tr>
<tr>
<td><code>$$</code></td>
<td>Le numéro de process du shell lui-même.</td>
</tr>
</table>
<div class="metainformation">
Basé sur un polycopié de Roberto Di Cosmo, Xavier Leroy et Damien Doligez.
Modifications&nbsp;: Nicolas George, Baptiste Mélès.
Dernière modification le <date value="$Date: 2005-05-31 11:33:38 $" />.
</div>
</body>
</html>