c7f4bbad36
Last-change: ignore this commit
590 lines
17 KiB
XML
590 lines
17 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE html
|
||
PUBLIC "-//ENS/Tuteurs//DTD TML 1//EN"
|
||
"tuteurs://DTD/tml.dtd">
|
||
<html>
|
||
<head>
|
||
<title>grep</title>
|
||
</head>
|
||
<body>
|
||
|
||
|
||
<h1>Exercices sur <code>grep</code> : corrigés</h1>
|
||
|
||
<p>
|
||
Ces exercices sont des questions de cours : les solutions se trouvent
|
||
toutes dans les pages de man des commandes en question. On suppose donc
|
||
connues les commandes de <code>less</code>, qui servent à se déplacer dans les
|
||
pages de man... et la commande servant à chercher un mot. Testez les
|
||
commandes sur des fichiers et répertoires d'essai pour vous faire la main et
|
||
comprendre ce qui se passe.
|
||
</p>
|
||
|
||
|
||
|
||
<h2><a name="options">Les options de <code>grep</code></a></h2>
|
||
|
||
|
||
<ol>
|
||
|
||
<li><a name="grep1"> <strong>Quelles sont les options de <code>grep</code>
|
||
qui permettent d'obtenir des lignes de contexte (qui précèdent et/ou suivent
|
||
la ligne où figure le mot) ? </strong> </a> <p>
|
||
|
||
Il y en a plusieurs, qui se recoupent :</p>
|
||
|
||
<ul>
|
||
<li> <code>-<em>num</em></code> : le <em>numéro</em> indique le nombre de
|
||
lignes de contexte que l'on veut voir figurer avant et après la ligne où
|
||
figure le mot recherché. Par exemple, si on veut trois lignes de contexte,
|
||
avant et après la mot (soit sept lignes au total), on tape :
|
||
|
||
<pre>
|
||
grep -3 ...
|
||
</pre>
|
||
</li>
|
||
|
||
<li> <code>-A <em>num</em></code> (<em>after</em>) : le <em>numéro</em>
|
||
indique le nombre de lignes qui doivent suivre la ligne où figure le mot. Si
|
||
on en veut quatre, on tapera :
|
||
|
||
|
||
<pre>
|
||
grep -A 4 ...
|
||
</pre>
|
||
</li>
|
||
|
||
|
||
<li> <code>-B <em>num</em></code> (<em>before</em>) : le
|
||
<em>numéro</em> indique le nombre de lignes qui doivent précéder la ligne où
|
||
figure le mot. Si on en veut dix, on tape :
|
||
|
||
<pre>
|
||
grep -B 10 ...
|
||
</pre>
|
||
</li>
|
||
|
||
<li> <code>-C</code> (<em>context</em>), qui donne deux lignes de contexte
|
||
avant et après. En fait, les trois lignes suivantes sont strictement
|
||
équivalentes :
|
||
<pre>
|
||
grep -2 ...
|
||
grep -C ...
|
||
grep -A 2 -B 2 ...
|
||
</pre>
|
||
</li>
|
||
|
||
</ul></li>
|
||
|
||
<li><a name="grep2"> <strong>Comment faire apparaître le numéro de la
|
||
ligne où figure le mot recherché ? </strong> </a>
|
||
<p>
|
||
C'est l'option <code>-n</code> (<em>number</em>) qui sert à cela; le numéro
|
||
figure tout au début de la ligne, suivi d'un deux-points (<code>:</code>) et
|
||
du texte. Par exemple : </p>
|
||
|
||
<pre>
|
||
<span class="prompt">bireme ~ $</span> grep -n violon verlaine.tex
|
||
12:des violons de l'automne
|
||
</pre>
|
||
<p>
|
||
Quand on fait une recherche dans plusieurs fichiers, le nom du fichier figure
|
||
d'abord, puis le numéro de la ligne, et enfin le texte, le tout séparé par des
|
||
deux-points. Par exemple :
|
||
</p>
|
||
|
||
<pre>
|
||
<span class="prompt">bireme ~ $</span> grep -n violon *
|
||
verlaine.tex:12:des violons de l'automne
|
||
orchestre:45:Cordes : contrebasse, violoncelle, alto, violons.
|
||
</pre>
|
||
|
||
<strong>Que se passe-t-il quand on demande également des lignes de
|
||
contexte ?</strong> <p>
|
||
|
||
La disposition générale ne change pas, par contre, le signe utilisé pour
|
||
séparer la ligne de son numéro est un tiret (<code>-</code>) quand il s'agit
|
||
des lignes de contexte, et un deux-points quand il s'agit de la ligne voulue.
|
||
Par exemple : </p>
|
||
|
||
<pre>
|
||
<span class="prompt">bireme ~ $"</span> grep -nC violon verlaine.tex
|
||
10-
|
||
11-Les sanglots longs
|
||
12:des violons de l'automne
|
||
13-bercent mon coeur
|
||
14-d'une langueur monotone
|
||
</pre>
|
||
</li>
|
||
|
||
<li><a name="grep22"> <strong> Comment faire pour afficher le nombre
|
||
d'occurences du mot recherché ? </strong> </a> <p>
|
||
|
||
On utilise l'option <code>-c</code> (<em>count</em>) :
|
||
</p>
|
||
|
||
<pre>
|
||
<span class="prompt">bireme ~ $</span> grep -c violon *
|
||
verlaine.tex:1
|
||
orchestre:1
|
||
</pre>
|
||
</li>
|
||
|
||
<li><a name="grep23"> <strong> Comment faire pour que <code>grep</code>
|
||
ignore la casse des caractères (différence entre majuscules et minuscules)
|
||
dans sa recherche ? </strong> </a>
|
||
<p>
|
||
Par défaut, <code>grep</code> fait la différence entre les majuscules et les
|
||
minuscules; pour invalider ce comportement, on utilise l'option
|
||
<code>-i</code> (<em>ignorecase</em>).</p>
|
||
</li>
|
||
|
||
<li><a name="grep4"> <strong>Comment faire pour faire apparaître non pas les
|
||
lignes où figurent le mot, mais les noms des fichiers ?</strong> </a>
|
||
<p>
|
||
C'est l'option <code>-l</code> qui permet de faire cela : afficher les
|
||
noms des fichiers où figure au moins une fois la chaîne de caractères
|
||
recherchée.</p></li>
|
||
|
||
<li><a name="grep5"> <strong>Comment faire apparaître les lignes où ne
|
||
figurent pas le mot recherché ? </strong> </a>
|
||
<p>
|
||
On veut en fait inverser le sens de la recherche : c'est l'option
|
||
<code>-v</code> qui fait cela.
|
||
</p></li>
|
||
|
||
<li><a name="grep51"> <strong> Comment faire apparaître les noms des fichiers
|
||
ne contenant pas le mot recherché ? </strong> </a>
|
||
<p>
|
||
On utilise l'option <code>-L</code>, qui affiche les noms de fichiers où ne
|
||
figurent pas la chaîne de caractères recherchée. Il ne faut bien sûr pas
|
||
confondre les options <code>-l</code> et <code>-L</code>...
|
||
</p></li>
|
||
|
||
<li><a name="grep6"> <strong>Comment faire pour que <code>grep</code> ne
|
||
recherche que les lignes où figure le mot tel quel, et non pas ses
|
||
variantes ? </strong> </a> <p>
|
||
|
||
C'est l'option <code>-w</code> (comme <em>word</em>) qui sert à cela : un
|
||
mot complet est délimité comme suit :
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li> Début : la chaîne de caractères est placée au début d'une ligne, ou
|
||
précédée d'un blanc, d'une tabulation ou d'une ponctuation.</li>
|
||
|
||
<li> Fin : la chaîne de caractère est placée en fin de ligne, ou suivie
|
||
d'un blanc, d'une tabulation ou d'une ponctuation.</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
Si donc on veut chercher «travail» et aucune forme dérivée de ce mot, on
|
||
écrit :
|
||
</p>
|
||
|
||
<pre>
|
||
grep -w travail mon-fichier
|
||
</pre>
|
||
</li>
|
||
|
||
<li><a name="grep3"> <strong>Comment faire pour chercher plusieurs mots à la
|
||
fois en faisant apparaître les numéros des lignes ?</strong> </a> <p>
|
||
On veut chercher toutes les occurences des mots «terre» et «ciel» dans les
|
||
deux premiers chapitres de la première partie de <em>Germinal</em>, avec les
|
||
numéros des lignes. On propose deux solutions, la première utilisant les
|
||
ressources de la syntaxe de <code>grep</code>, la seconde utilisant l'option
|
||
<code>-f</code> avec un fichier.</p>
|
||
|
||
<ol>
|
||
|
||
<li> <strong>Syntaxe de <code>grep</code></strong> : La structure
|
||
<code>\(mot1\|mot2\)</code> permet de chercher plusieurs mots. Ici, on tape la
|
||
ligne suivante :
|
||
|
||
<pre>
|
||
grep '\(ciel\|terre\)' <em>fichier</em>
|
||
</pre>
|
||
|
||
<p>
|
||
On met des apostrophes de part et d'autre de l'expression pour la protéger
|
||
contre le shell, c'est-à-dire pour que le shell ne cherche pas à interpréter
|
||
l'expression.
|
||
</p></li>
|
||
|
||
<li> <strong>Option «<code>-f</code> <em>fichier</em>»</strong> : dans un
|
||
fichier quelconque, que nous appellerons <code>liste</code>, on indique les
|
||
mots que l'on recherche : «ciel» et «terre». Chaque ligne correspond à un
|
||
mot recherché. Il ne faut donc pas mettre de ligne comme
|
||
|
||
<pre>
|
||
terre ciel
|
||
</pre>
|
||
|
||
|
||
<p class="continue">
|
||
car le programme chercherait la chaîne de caractères «terre ciel», qui est
|
||
assez improbable en français. Il ne faut pas non plus laisser de ligne
|
||
blanche : le programme afficherait l'ensemble du texte.
|
||
</p>
|
||
</li>
|
||
</ol>
|
||
|
||
<p>
|
||
Quelle que soit la solution retenue, on veut ensuite afficher le numéro des
|
||
lignes (option <code>-n</code>); d'autre part, pour que la recherche soit
|
||
exhaustive, il vaut mieux que <code>grep</code> ne fasse pas de différence
|
||
entre les majuscules et les minuscules, avec l'option <code>-i</code>
|
||
(<em>ignore case</em>, ignorer la casse des caractères). Il faut aussi décider
|
||
si on cherche les mots tels quels, sans leurs variantes (comme «terre» au
|
||
pluriel), ou si on accepte ces variantes. Si on ne veut que le mot sans ses
|
||
dérivés, on utilise l'option <code>-w</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Pour désigner les deux fichiers où faire la recherche, on peut les écrire
|
||
littéralement :
|
||
</p>
|
||
|
||
<pre>
|
||
zola1.txt zola2.txt
|
||
</pre>
|
||
<p class="continue">
|
||
ou, mieux, utiliser les joker du shell :
|
||
</p>
|
||
<pre>
|
||
zola[12].txt
|
||
</pre>
|
||
|
||
<p><code>[12]</code> signifie «le caractère 1 ou le caractère 2».</p>
|
||
|
||
<p>
|
||
<strong>Finalement, on peut taper, au choix</strong> : </p>
|
||
<pre>
|
||
grep -inw -f liste zola1.txt zola2.txt
|
||
grep -inw -f liste zola[12].txt
|
||
grep -inw '\(ciel\|terre\)' zola1.txt zola2.txt
|
||
grep -inw '\(ciel\|terre\)' zola[12].txt
|
||
</pre>
|
||
|
||
<p>
|
||
Et on obtient :
|
||
</p>
|
||
|
||
<pre>
|
||
zola1.txt:13:ciel, le pavé se déroulait avec la rectitude d'une jetée, au
|
||
milieu de
|
||
zola1.txt:36:brûlaient si haut dans le ciel mort, pareils à des lunes fumeuses.
|
||
Mais, au
|
||
zola1.txt:50:besogne. Les ouvriers de la coupe à terre avaient dû travailler
|
||
tar d, on
|
||
zola1.txt:124:terre, lorsqu'un accès de toux annonça le retour du charretier.
|
||
Le ntement,
|
||
zola1.txt:191:bleues en plein ciel, comme des torches géantes. C'était d'une
|
||
tristesse
|
||
zola1.txt:207: Le manoeuvre, après avoir vidé les berlines, s'était assis à
|
||
terre,
|
||
zola1.txt:222:fois avec tout le poil roussi, une autre avec de la terre jusque
|
||
dans le
|
||
|
||
(...)
|
||
</pre>
|
||
|
||
<p>
|
||
Le résultat est un peu différent quand on n'utilise pas l'option
|
||
<code>-w</code>.</p>
|
||
</li>
|
||
|
||
</ol>
|
||
|
||
<h2><a name="regexp">Introduction aux expressions régulières</a></h2>
|
||
|
||
<p>
|
||
<code>grep</code> recherche des chaînes de caractères, qui peuvent être un mot
|
||
complet («terre»), une suite de lettres («tre»), ou une expression régulière.
|
||
Les expressions régulières sont des formules qui représentent des chaînes de
|
||
caractères. On cherche alors non pas un mot précis, mais des suites de
|
||
caractères correspondant aux critères demandés. Elles sont d'un usage fréquent
|
||
avec <code>grep</code> bien sûr, mais aussi avec des commandes comme
|
||
<code>less</code>, ou encore au sein d'un éditeur.
|
||
</p>
|
||
|
||
<p>
|
||
«Expressions régulières» (<em>Regular expressions</em> en anglais) se
|
||
traduisent en bon français par «expressions rationnelles», mais l'usage est de
|
||
dire «régulières».
|
||
</p>
|
||
|
||
<p>
|
||
Ces exercices n'entendent pas remplacer un cours sur les expressions
|
||
régulières, ni faire le tour de leurs possibilités.
|
||
</p>
|
||
|
||
<ol>
|
||
<li><a name="reg1"> <strong>Chercher toutes les lignes commençant par «a» ou
|
||
«A»</strong> </a>. <p>
|
||
Il faut indiquer que l'on veut le début de la ligne, avec le chapeau
|
||
(<em>caret</em> en anglais). Ensuite, on veut préciser que la ligne commence
|
||
par un «a» minuscule ou majuscule. Il y a deux façons de le faire :
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li> Utiliser l'option <code>-i</code> qui fait ignorer la différence entre
|
||
les majuscules et le minuscules.</li>
|
||
|
||
<li> Dire que l'on cherche un «a» ou un «A». C'est à cela que servent les
|
||
crochets : <code>[abc]</code> signifie «a ou b ou c». Ici, ce sera
|
||
<code>[aA]</code>.</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
Enfin, il faut protéger les signes contre le shell, pour qu'il ne les
|
||
interprète pas; on met donc l'expression entre apostrophes. Remarque :
|
||
la protection des expressions régulières contre le shell est une question
|
||
complexe....
|
||
</p>
|
||
|
||
<p>
|
||
Il faut donc écrire :
|
||
</p>
|
||
|
||
<pre>
|
||
grep -i '^a' fichier
|
||
</pre>
|
||
|
||
<p class="continue">
|
||
ou
|
||
</p>
|
||
|
||
<pre>
|
||
grep '^[aA]' fichier
|
||
</pre>
|
||
</li>
|
||
|
||
|
||
<li><a name="reg2"> <strong>Chercher toutes les lignes finissant par
|
||
«rs»</strong> </a> <p>
|
||
|
||
|
||
C'est le dollar (<code>$</code>) qui représente la fin de la ligne. Il faut
|
||
donc écrire : </p>
|
||
|
||
<pre>
|
||
grep 'rs$' fichier
|
||
</pre></li>
|
||
|
||
<li><a name="reg3"> <strong>Chercher toutes les lignes contenant au moins un
|
||
chiffre</strong> </a> <p>
|
||
|
||
|
||
Pour désigner un chiffre, on peut en indiquer une liste entre crochets :
|
||
<code>[0123456789]</code>. Il est plus simple d'utiliser une classe de
|
||
caractères : <code>[0-9]</code> qui désigne, comme la solution précédente,
|
||
n'importe quel chiffre de zéro à neuf.
|
||
</p>
|
||
<p>
|
||
Il faut donc taper : </p>
|
||
<pre>
|
||
grep '[0-9]' fichier
|
||
</pre>
|
||
</li>
|
||
|
||
<li><a name="reg4"> <strong>Chercher toutes les lignes commençant par une
|
||
majuscule</strong> </a> <p>
|
||
|
||
|
||
Comme on l'a vu, c'est le chapeau qui indique le début de la ligne. Pour
|
||
indiquer que l'on cherche une majuscule, on peut soit en donner une liste
|
||
(<code>[ABCDEFGHIJKLMNOPQRSTUVWXYZ]</code>), soit utiliser une classe de
|
||
caractères : <code>[A-Z]</code>, la seconde solution étant, de loin,
|
||
préférable...
|
||
</p>
|
||
<p>
|
||
Il faut donc taper :
|
||
</p>
|
||
<pre>
|
||
grep '^[A-Z]' fichier
|
||
</pre>
|
||
</li>
|
||
|
||
<li><a name="reg5"> <strong>Chercher toutes les lignes commençant par «B»,
|
||
«E» ou «Q»</strong> </a> <p>
|
||
|
||
|
||
Il faut indiquer entre crochets les trois lettres recherchées :
|
||
</p>
|
||
|
||
<pre>
|
||
grep '^[BEQ]' fichier
|
||
</pre>
|
||
</li>
|
||
|
||
|
||
<li><a name="reg6"> <strong>Chercher toutes les lignes finissant par un point
|
||
d'exclamation</strong> </a> <p>
|
||
|
||
|
||
Le point d'exclamation n'a pas de signification particulière avec
|
||
<code>grep</code>, on peut donc le mettre tel quel :
|
||
</p>
|
||
|
||
<pre>
|
||
grep '!$' fichier
|
||
</pre>
|
||
</li>
|
||
|
||
<li><a name="reg7"> <strong>Chercher toutes les lignes ne finissant pas par
|
||
un signe de ponctuation (point, virgule, point-virgule, deux-points, point
|
||
d'interrogation, point d'exclamation)</strong> </a><p>
|
||
|
||
|
||
Il faut donner une liste de caractères, que l'on ne veut pas voir figurer; la
|
||
liste sera entre crochets, comme on l'a déjà vu, et c'est le chapeau qui
|
||
signifiera, dans ce contexte, «sauf». Par exemple, si on cherche tous les «a»,
|
||
sauf ceux suivi de «b», «c» ou «t», on écrit :
|
||
</p>
|
||
|
||
<pre>
|
||
grep 'a[^bct]'
|
||
</pre>
|
||
|
||
|
||
<p>
|
||
Il y a une seconde difficulté, qui vient de ce que certains caractères sont
|
||
spéciaux avec <code>grep</code>. Vous vous doutez que le chapeau est spécial
|
||
quand il est placé au début de l'expression, et que le dollar l'est quand il
|
||
est placé en fin d'expression. Dans notre cas :
|
||
</p>
|
||
<ul>
|
||
|
||
<li> Le point désigne n'importe quel caractère.</li>
|
||
|
||
<li> Le point d'interrogation signifie «le caractère qui précède apparaît 0 ou
|
||
1 fois». Avec <code>egrep</code>, il fonctionne tout seul, avec
|
||
<code>grep</code>, il faut le faire précéder d'un backslash pour qu'il
|
||
fonctionne; par exemple (avec <code>grep</code>), pour chercher «charbon» ou
|
||
«vagabond», on écrit :
|
||
<pre>
|
||
grep 'ar?bo' fichier
|
||
</pre>
|
||
|
||
<p class="continue">
|
||
(chercher la suite de lettre «abo» avec un «r» facultatif entre le «a» et le
|
||
«b»).</p>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
Pour que <code>grep</code> interprète littéralement ces caractères, et ne les
|
||
considère plus comme spéciaux, il faut les faire précéder d'un backslash
|
||
(<code>\</code>). Si par exemple vous cherchez toutes les lignes qui se
|
||
terminent par un point, il faut taper :
|
||
</p>
|
||
|
||
<pre>
|
||
grep '\.$' fichier
|
||
</pre>
|
||
|
||
|
||
<p>
|
||
Dans notre cas cependant, ces caractères sont protégés par les crochets. On
|
||
peut donc écrire :
|
||
</p>
|
||
|
||
<pre>
|
||
grep '[^.,;:?!]$' fichier
|
||
</pre>
|
||
|
||
|
||
<p>
|
||
On peut aussi utiliser l'option <code>-v</code>, qui prend toutes les lignes
|
||
où ne figure pas une chaîne de caractères donnée; dans ce cas, on tape :
|
||
</p>
|
||
|
||
<pre>
|
||
grep -v '[.,;:?!]$' fichier
|
||
</pre>
|
||
</li>
|
||
|
||
|
||
<li><a name="reg8"> <strong>Comment chercher tous les mots contenant un «r»
|
||
précédé de n'importe quelle lettre majuscule ou minuscule ? </strong>
|
||
</a> <p>
|
||
|
||
On cherche une chaîne de caractères qui soit indifféremment au début ou au
|
||
milieu d'un mot. N'importe quelle lettre, ce sont les classes de caractères
|
||
<code>[a-zA-Z]</code> ou <code>[:alpha:]</code>, qui sont équivalentes.
|
||
</p>
|
||
|
||
<p>
|
||
Il y a une petite subtilité avec l'emploi de classes du second type; elles
|
||
désignent un groupe de caractères, et il faut mettre une seconde paire de
|
||
crochets pour dire «n'importe quel caractère de cette classe prédéfinie».
|
||
On tape donc au choix :
|
||
</p>
|
||
|
||
<pre>
|
||
grep '[a-zA-Z]r' fichier'
|
||
</pre>
|
||
<p class="continue">
|
||
ou
|
||
</p>
|
||
<pre>
|
||
grep '[[:alpha:]]r' fichier'
|
||
</pre>
|
||
|
||
|
||
<p>
|
||
Attention, dans ces listes ne sont pas compris les caractères accentués...
|
||
</p></li>
|
||
|
||
<li><a name="reg9"> <strong>Chercher tous les mots dont la seconde lettre est
|
||
un «r»</strong> </a>.
|
||
|
||
<p>
|
||
C'est le symbole <code>\<</code> qui désigne un début de mot. La première
|
||
lettre du mot est indifférente, la seconde est un «r». On écrit donc :
|
||
</p>
|
||
<pre>
|
||
grep '\<.r' fichier
|
||
</pre>
|
||
|
||
|
||
<p>
|
||
Il y a cependant un problème avec les caractères accentués, que
|
||
<code>grep</code> considère comme des blancs. Dans ce cas, il vaut mieux
|
||
procéder autrement : un mot est précédé d'un début de ligne, ou d'un
|
||
blanc ou d'une tabulation. Un début de ligne, c'est le chapeau, un blanc ou
|
||
une tabulation, c'est la classe de caractères <code>[:space:]</code>.
|
||
</p>
|
||
<p>
|
||
On va se servir du pipe (<code>|</code>) qui signifie «ou». Avec
|
||
<code>grep</code>, il faut backslasher le pipe, avec <code>egrep</code> ce
|
||
n'est pas nécessaire. On écrit donc (avec <code>grep</code>) :
|
||
</p>
|
||
|
||
<pre>
|
||
grep '^.r|[[:space:]].r' fichier
|
||
</pre>
|
||
|
||
<p>
|
||
Ce n'est quand même pas si simple; les mots peuvent être précédés d'un tiret
|
||
(mots composés), d'une apostrophe, de guillemets divers (<code>``</code>,
|
||
<code>"</code>, <code>«</code>, <code><<</code>), et, si l'auteur du
|
||
texte n'est pas respectueux des règles de typographie, d'une ponctuation. Il y
|
||
a donc bien des cas à envisager...
|
||
</p>
|
||
</li>
|
||
</ol>
|
||
|
||
<div class="metainformation">
|
||
Auteur : Émilia Robin, Joël Riou. Dernière modification le <date value="$Date: 2007-07-17 10:03:37 $"/>.
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|
||
|