tuteurs.ens.fr/unix/shell/test.tml

425 lines
12 KiB
Text
Raw Normal View History

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
"tuteurs://DTD/tml.dtd">
<html>
<head>
<title>Tests</title>
</head>
<body>
<h1>Tests et calcul arithm<68>tique</h1>
<h2>Qu'est-ce qu'un test&nbsp;?</h2>
<p>
<strong>Un test est une op<6F>ration dont le but est d'<27>valuer la valeur
d'une expression</strong>. Cette expression peut <20>tre simplement
l'<em>existence</em> de quelque chose (par exemple, d'un fichier, ou
bien d'une variable), ou ce peut <20>tre une <em>proposition</em>.</p>
<p>Concr<63>tement, cela veut dire qu'<strong>un programme n'est pas limit<69>
<EFBFBD> la possibilit<69> de donner des ordres&nbsp;: il peut aussi poser des
questions</strong>, et agir comme vous le d<>cidez en fonction des
r<EFBFBD>ponses apport<72>es aux questions.</p>
<p>Posons par exemple le principe suivant&nbsp;: <20>&nbsp;Si Dieu n'existe
pas, alors tout est permis.&nbsp;<3B> Nous allons ensuite tester si Dieu
existe, et s'il n'existe pas, nous saurons que tout est permis. Autre
exemple&nbsp;: posons que <20>&nbsp;si le train passe sous le tunnel avant
que j'aie compt<70> jusqu'<27> dix, alors Manek est vivant.&nbsp;<3B> Je vais
donc tester si le train passe sous le tunnel avant que j'aie compt<70>
jusqu'<27> dix, et si c'est le cas, alors c'est que Manek est vivant. C'est
aussi simple que cela.</p>
<h3>Une condition</h3>
2005-09-07 23:19:24 +02:00
<p>Le shell propose deux principales fa<66>ons de r<>aliser un test&nbsp;;
ces deux m<>thodes sont <20>quivalentes&nbsp;:</p>
<ul>
<li> <code>test <em>expression</em></code></li>
<li> <code>[ <em>expression</em> ]</code></li>
</ul>
<p>
2005-09-07 23:19:24 +02:00
Les deux commandes suivantes reviennent donc au m<>me&nbsp;:</p>
<pre>
<span class="prompt">clipper ~ $</span>&nbsp;<strong>test -f foo</strong> || echo &quot;Le fichier foo n'existe pas.&quot;
Le fichier foo n'existe pas.
<span class="prompt">clipper ~ $</span>&nbsp;<strong>[ -f foo ]</strong> || echo &quot;Le fichier foo n'existe pas.&quot;
Le fichier foo n'existe pas.
</pre>
<h3>Code de retour d'un test</h3>
<p>Un test renvoie un <em>code de retour</em>. <strong>Un code de retour
2005-09-07 23:19:24 +02:00
est un nombre (0 ou&nbsp;autre), qui correspond <20> une r<>ponse de
type <20>&nbsp;vrai&nbsp;<3B> ou <20>&nbsp;faux&nbsp;<3B></strong>. C'est ce code de
retour qui permet la manipulation des tests dans des structures de
contr<EFBFBD>le comme <code>if</code>, etc. </p>
2005-09-07 23:19:24 +02:00
<p>
Le code de retour&nbsp;0 correspond <20> la r<>ponse
<EFBFBD>&nbsp;vrai&nbsp;<3B>. Pour r<>pondre <20>&nbsp;faux&nbsp;<3B>, le programme
r<EFBFBD>pond... autre chose (ce peut <20>tre&nbsp;1, 2, -1 ou autre).
</p>
<p>Par exemple, un pilote de Formule&nbsp;1 utilise le programme
suivant&nbsp;:</p>
<pre>
while [[ &quot;$couleur_du_feu&quot; != &quot;vert&quot; ]]
do attendre
done
d<EFBFBD>marrer_en_trombe
</pre>
<h2>Les op<6F>rateurs</h2>
<h3>Op<4F>rateurs logiques</h3>
<h4>Non&nbsp;: l'op<6F>rateur <20>&nbsp;<code>!</code>&nbsp;<3B></h4>
<p><strong>L'op<6F>rateur logique <20>&nbsp;non&nbsp;<3B> inverse le code de
retour d'une commande</strong>, c'est-<2D>-dire renvoie vrai si elle
renvoie faux, et vice versa. </p>
<p>On utilise cet op<6F>rateur en pr<70>c<EFBFBD>dant une condition d'un point
d'exclamation (<28>&nbsp;!&nbsp;<3B>). </p>
<h5>Comparaison de plusieurs combinaisons d'op<6F>rateurs</h5>
<p>Pour illustrer l'usage de cet op<6F>rateur, voici quatre cas de figure
diff<EFBFBD>rents&nbsp;:</p>
<pre>
# Premier cas
[ <strong>-f</strong> foo ] <strong>&amp;&amp;</strong> echo &quot;Le fichier foo existe.&quot;
</pre>
<p class="continue">Dans l'exemple pr<70>c<EFBFBD>dent, le shell teste si le
fichier <code>foo</code> existe. Comme il n'existe pas, le code de
retour de ce test est&nbsp;0. Or, l'op<6F>rateur <20>&nbsp;&amp;&amp;&nbsp;<3B>
n'ex<65>cute ce qui suit que si le code de retour est&nbsp;1. Comme ce
n'est pas le cas, les commandes ne sont pas ex<65>cut<75>es.</p>
<pre>
# Deuxi<78>me cas
[ <strong>-f</strong> foo ] <strong>||</strong> echo &quot;Le fichier foo n'existe pas.&quot;
Le fichier foo n'existe pas.
</pre>
<p class="continue">Dans cet exemple, l'op<6F>rateur n'est plus
<EFBFBD>&nbsp;&amp;&amp;&nbsp;<3B> mais <20>&nbsp;||&nbsp;<3B>. Les commandes ne
s'ex<65>cutent que si le code de retour vaut 0&nbsp;; comme c'est le cas,
elles sont ex<65>cut<75>es.</p>
<pre>
# Troisi<73>me cas
2005-09-08 01:00:03 +02:00
[ <strong>! -f</strong> foo ] <strong>&amp;&amp;</strong> echo &quot;Le fichier foo n'existe pas.&quot;
Le fichier foo n'existe pas.
</pre>
<p class="continue">Ici, l'op<6F>rateur est de nouveau
<EFBFBD>&nbsp;&amp;&amp;&nbsp;<3B>&nbsp;; mais contrairement aux deux exemples
pr<EFBFBD>c<EFBFBD>dents, le test n'est plus <code>[&nbsp;-f&nbsp;foo&nbsp;]</code>
mais <code>[&nbsp;!&nbsp;-f&nbsp;foo&nbsp;]</code>. Par cons<6E>quent, le
code de retour est&nbsp;1, et les commandes sont ex<65>cut<75>es.</p>
<pre>
# Quatri<72>me cas
2005-09-08 01:00:03 +02:00
[ <strong>! -f</strong> foo ] <strong>||</strong> echo &quot;Le fichier foo existe.&quot;
</pre>
<p class="continue">Voici la derni<6E>re combinaison possible. Le code de
retour est&nbsp;1, mais il fallait&nbsp;0 pour que les commandes soient
ex<EFBFBD>cut<EFBFBD>es. </p>
<p>Ces quatre exemples correspondent aux <20>nonc<6E>s suivants&nbsp;:</p>
<ul>
<li> <em>s'il est vrai que <code>foo</code> existe</em>, alors il faut
<EFBFBD>crire&nbsp;: <20>&nbsp;Le fichier foo existe.&nbsp;<3B>.</li>
<li> <em>s'il n'est pas vrai que <code>foo</code> existe</em>, alors il
faut <20>crire&nbsp;: <20>&nbsp;Le fichier foo n'existe pas.&nbsp;<3B>.</li>
<li> <em>s'il est vrai que <code>foo</code> n'existe pas</em>, alors il
faut <20>crire&nbsp;: <20>&nbsp;Le fichier foo n'existe pas.&nbsp;<3B>.</li>
<li> <em>s'il n'est pas vrai que <code>foo</code> n'existe pas</em>,
alors il faut <20>crire&nbsp;: <20>&nbsp;Le fichier foo existe.&nbsp;<3B>.</li>
</ul>
<h5>Reformulation avec <code>if</code></h5>
<p>Dans un script, outre la formulation pr<70>c<EFBFBD>dente, on pourra
<EFBFBD>crire&nbsp;:</p>
<pre>
# Premier cas
if [ -f foo ]
then echo &quot;Le fichier foo existe.&quot;
else continue
fi
# Deuxi<78>me cas
if [ -f foo ]
then continue
else echo &quot;Le fichier foo n'existe pas.&quot;
fi
# Troisi<73>me cas
if [ ! -f foo ]
then echo &quot;Le fichier foo n'existe pas.&quot;
else continue
fi
# Quatri<72>me cas
if [ ! -f foo ]
then continue
else echo &quot;Le fichier foo existe.&quot;
fi
</pre>
<h4>Et&nbsp;: l'op<6F>rateur <20>&nbsp;<code>-a</code>&nbsp;<3B></h4>
<p>L'op<6F>rateur <20>&nbsp;et&nbsp;<3B> renvoie&nbsp;1 (vrai) si et seulement si
les diff<66>rentes conditions sont toutes r<>alis<69>es&nbsp;; si au moins
l'une d'entre elles ne l'est pas, le code de retour est&nbsp;0
(faux). On note cet op<6F>rateur en ins<6E>rant <20>&nbsp;-a&nbsp;<3B> entre les
diff<EFBFBD>rentes conditions. Exemples&nbsp;: </p>
<pre>
touch foo # donc foo existe
rm bar # donc bar n'existe pas
# [ -f foo ] = vrai si le fichier foo existe
# [ ! -f bar ] = vrai si bar n'existe pas
# <20> n'ex<65>cuter que si foo existe ET que bar n'existe pas.
[ -f foo <strong>-a</strong> ! -f bar ] &amp;&amp;
mv foo bar
</pre>
<p class="continue">Autres formulations possibles&nbsp;:
</p>
<pre>
test -f foo -a ! -f bar
[ -f foo ] -a [ ! -f bar ]
[[ -f foo &amp;&amp; ! -f bar ]]
</pre>
<p><strong>Si vous d<>butez, vous n'<27>tes pas tenu de retenir par
c&oelig;ur toutes les combinaisons possibles. Sachez simplement les
reconna<EFBFBD>tre</strong> si vous les lisez quelque part&nbsp;; et pour vos
propres scripts, vous il vous suffit de savoir bien manipuler la syntaxe
qui vous para<72>t la plus lisible.</p>
<h4>Ou&nbsp;: l'op<6F>rateur <20>&nbsp;<code>-o</code>&nbsp;<3B></h4>
<p>Pour r<>aliser la condition de l'op<6F>rateur <20>&nbsp;ou&nbsp;<3B>, il suffit
qu'une seule des conditions qu'il rassemble soit vraie&nbsp;: </p>
<ul>
<li> si <em>toutes</em> les conditions sont rassembl<62>es, la condition
d'ensemble l'est aussi&nbsp;;</li>
<li> si <em>une partie seulement</em> de ces conditions est rassembl<62>e,
la condition d'ensemble l'est aussi&nbsp;;</li>
<li> si <em>aucune</em> des conditions incluses n'est remplie, la
condition d'ensemble ne l'est pas non plus.</li>
</ul>
<p>Exemple&nbsp;:</p>
<pre>
if [[ &quot;$fichier&quot; == &quot;fichier_interdit&quot; -o ! -f &quot;$fichier&quot; ]]
then echo &quot;Je ne veux pas lire $fichier ou bien il n'existe pas.&quot;
fi
</pre>
<ul>
<li> si <code>$fichier</code> vaut
<code>&quot;fichier_interdit&quot;</code>, il n'est pas lu&nbsp;;</li>
<li> si <code>$fichier</code> n'existe pas, il n'est pas lu&nbsp;;</li>
<li> si <code>$fichier</code> vaut
<code>&quot;fichier_interdit&quot;</code> et qu'en plus il n'existe pas,
il n'est pas lu&nbsp;;</li>
<li> si <code>$fichier</code> ne vaut pas
<code>&quot;fichier_interdit&quot;</code> et qu'il existe bien, il est
lu.</li> </ul>
<h3>Op<4F>rateurs arithm<68>tiques</h3>
<p>
Le shell permet d'op<6F>rer des calculs arithm<68>tiques, m<>me s'il est moins
puissant que d'autres langages (Perl, Scheme, C, etc.) pour cela.
</p>
<p>
Les op<6F>rateurs sont les suivants&nbsp;:
</p>
<ul>
<li> <code>-eq</code> (<i>equal</i>)&nbsp;: <20>&nbsp;<3B>gal <20>&nbsp;<3B> (signe
<EFBFBD>&nbsp;=&nbsp;<3B>)&nbsp;;</li>
<li> <code>-ne</code> (<i>not equal</i>)&nbsp;: <20>&nbsp;diff<66>rent
de&nbsp;<3B> (signe <20>&nbsp;&ne;&nbsp;<3B>)&nbsp;;</li>
<li> <code>-gt</code> (<i>greater than</i>)&nbsp;: <20>&nbsp;strictement
sup<EFBFBD>rieur <20>&nbsp;<3B> (signe <20>&nbsp;&gt;&nbsp;<3B>)&nbsp;;</li>
<li> <code>-lt</code> (<i>lesser than</i>)&nbsp;: <20>&nbsp;strictement
inf<EFBFBD>rieur <20>&nbsp;<3B> (signe <20>&nbsp;&lt;&nbsp;<3B>)&nbsp;;</li>
<li> <code>-ge</code> (<i>greater or equal</i>)&nbsp;: <20>&nbsp;sup<75>rieur
ou <20>gal <20>&nbsp;<3B> (signe <20>&nbsp;&ge;&nbsp;<3B>)&nbsp;;</li>
<li> <code>-le</code> (<i>lesser or equal</i>)&nbsp;: <20>&nbsp;inf<6E>rieur
ou <20>gal <20>&nbsp;<3B> (signe <20>&nbsp;&le;&nbsp;<3B>)&nbsp;;</li>
</ul>
<p>
On utilise ces op<6F>rateurs entre deux nombres ou variables
num<EFBFBD>riques. Par exemple&nbsp;:
</p>
<pre>
#!/bin/sh
if test 2 -lt 3
then echo &quot;C'est normal.&quot;
fi
if test 2 -gt 3
then echo &quot;C'est absurde.&quot;
fi
petit=2
grand=3
if test $petit -ne 3
then echo &quot;C'est normal.&quot;
fi
if test 2 -eq $grand
then echo &quot;C'est absurde.&quot;
fi
</pre>
<p class="continue">
Si vous ex<65>cutez ce programme, vous obtenez&nbsp;:
</p>
<pre>
C'est normal.
C'est normal.
</pre>
<h3>Op<4F>rateurs sur les fichiers</h3>
<p>
Une grande partie de la puissance du shell se d<>ploie dans sa facult<6C> de
manipuler des fichiers.
</p>
<p>
Les principaux op<6F>rateurs disponibles sont&nbsp;:
</p>
<ul>
<li> nature du fichier&nbsp;:
<ul>
<li> <code>-e</code> (<i>exists</i>)&nbsp;: v<>rifie l'existence d'un
fichier&nbsp;;</li>
<li> <code>-f</code> (<i>file</i>)&nbsp;: v<>rifie l'existence d'un
fichier, et le fait qu'il s'agisse bien d'un fichier au sens
strict&nbsp;;</li>
<li> <code>-d</code> (<i>directory</i>)&nbsp;: v<>rifie l'existence d'un
r<EFBFBD>pertoire&nbsp;;</li>
<li> <code>-L</code> (<i>link</i>)&nbsp;: v<>rifie si le fichier est un
lien symbolique&nbsp;;</li>
</ul></li>
<li>attributs du fichier&nbsp;:
<ul>
<li> <code>-s</code> (<i>size</i>)&nbsp;: v<>rifie qu'un fichier n'est
pas vide&nbsp;;</li>
</ul></li>
<li>droits sur le fichier&nbsp;:
<ul>
<li> <code>-r</code> (<i>readable</i>)&nbsp;: v<>rifie si un fichier peut
<EFBFBD>tre lu&nbsp;;</li>
<li> <code>-w</code> (<i>writable</i>)&nbsp;: v<>rifie si un fichier peut
<EFBFBD>tre <20>crit ou modifi<66>&nbsp;;</li>
<li> <code>-x</code> (<i>writable</i>)&nbsp;: v<>rifie si un fichier peut
<EFBFBD>tre ex<65>cut<75>&nbsp;;</li>
</ul></li>
<li>comparaison de fichiers&nbsp;:
<ul>
<li> <code>-nt</code> (<i>newer than</i>)&nbsp;: v<>rifie si un fichier
est plus r<>cent qu'un autre&nbsp;;</li>
<li> <code>-ot</code> (<i>older than</i>)&nbsp;: v<>rifie si un fichier
est plus ancien qu'un autre.</li>
</ul>
</li>
</ul>
<p>Exemple&nbsp;:</p>
<pre>
#!/bin/sh
if test -e ~/.emacs
then echo &quot;~/.emacs existe.&quot;
else echo &quot;~/.emacs n'existe pas.&quot;
fi
if test -d ~/.emacs
then echo &quot;~/.emacs est un r<>pertoire.&quot;
else echo &quot;~/.emacs n'est pas un r<>pertoire.&quot;
fi
if test -f ~/.emacs
then echo &quot;~/.emacs est un fichier.&quot;
else echo &quot;~/.emacs n'est pas un fichier.&quot;
fi
if test ~/.vimrc -nt ~/.emacs
then &quot;~/.vimrc est plus r<>cent que ~/.emacs.&quot;
fi
</pre>
<div class="metainformation">
Auteur<EFBFBD>: Baptiste M<>l<EFBFBD>s.
2005-09-08 01:00:03 +02:00
Derni<EFBFBD>re modification le <date value="$Date: 2005-09-07 23:00:05 $" />.
</div>
</body>
</html>