tuteurs.ens.fr/unix/exercices/solutions/enchainer-sol.tml

1208 lines
36 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>Pipes</title>
</head>
<body>
<h1><em>Pipes</em>, jokers et redirections<6E>: corrig<69>s</h1>
<h2><a name="cat">La commande <tt>cat</tt></a></h2>
<!-- ##########################################################<23>!-->
<ol>
<li> <strong> <a name="cat1">Comment visualiser deux fichiers l'un apr<70>s
l'autre <20> l'aide de la commande <tt>cat</tt><3E>? </a> </strong><p>
Il suffit de taper les deux noms de fichiers <20> la suite; par exemple, pour
visualier <tt>bla</tt> puis <tt>blo</tt>, on tape<70>:</p>
<pre>
cat bla blo
</pre></li>
<li> <strong> <a name="cat2">Comment faire une copie d'un fichier sans
utiliser <tt>cp</tt> ni ouvrir d'<27>diteur de texte<74>? </a> </strong>
<p>
On demande <20> <tt>cat</tt> d'afficher le contenu d'un fichier, puis de placer
le r<>sultat dans un fichier<65>:</p>
<pre>
cat toto &gt; copie
</pre></li>
<li> <strong> <a name="cat3">Utiliser <tt>cat</tt> pour <20>crire un peu de
texte et mettre le r<>sultat dans un fichier <tt>notes</tt>. </a> </strong>
<p>
On demande <20> <tt>cat</tt> de rediriger sa sortie dans un fichier, puis on n'a
plus qu'<27> taper le texte et <20> indiquer la fin du texte avec <tt>^D</tt><3E>:
</p>
<pre>
<span class="prompt">galion ~ $</span><3E>ls
maitrise.tex
<span class="prompt">galion ~ $</span><3E>cat &gt; notes
Faire la bibliographie
Revoir le chapitre 1.
^D
<span class="prompt">galion ~ $</span><3E>ls
maitrise.tex notes
<span class="prompt">galion ~ $</span><3E>cat notes
Faire la bibliographie
Revoir le chapitre 1.
<span class="prompt">galion ~ $</span>
</pre>
<p>
C'est bien s<>r un moyen un peu spartiate pour <20>crire du texte, mais dans le
cas de choses tr<74>s courtes dans ce style, ce peut <20>tre plus rapide que de
lancer un v<>ritable <20>diteur.
</p></li>
<li> <strong> <a name="cat4">Quelle diff<66>rence y a-t-il entre </a> </strong>
<pre>cat bla blo &gt; blu </pre> <p class="continue"><strong>et</strong></p>
<pre>cat bla blo &gt;&gt; blu </pre>
<p>
<strong>Que se passe-t-il, pour chaque ligne, selon que le fichier <tt>blu</tt>
existe ou n'existe pas<61>?</strong></p>
<pre>cat bla blo &gt; blu </pre> <p class="continue">
concat<EFBFBD>ne les deux fichiers <tt>bla</tt> et
<tt>blo</tt>, et place le r<>sultat dans un nouveau fichier appel<65>
<tt>blu</tt>. Si <tt>blu</tt> existe d<>j<EFBFBD>, le shell affiche un message
d'erreur et ne fait rien.</p>
<p>
<tt>cat bla blo &gt;&gt; blu </tt> place la concat<61>nation de <tt>bla</tt> et
<tt>blo</tt> <20> la fin d'un fichier <tt>blu</tt> d<>j<EFBFBD> existant. S'il n'existe
pas, le shell affiche un message d'erreur et en reste l<>, sans cr<63>er de
fichier <tt>blu</tt>. </p>
<p>
Les deux redirections ne sont donc pas du tout <20>quivalentes, mais leur action
d<EFBFBD>pend du shell, qui a des options qui modifie leur comportement par d<>faut.
</p></li>
<li> <strong> <a name="cat5">Comment obtenir un fichier <tt>blo</tt> qui
corresponde <20> un fichier <tt>bla</tt> dont les lignes seraient d<>sormais
num<EFBFBD>rot<EFBFBD>es<EFBFBD>? </a> </strong>
<p>
Il faut utiliser l'option <tt>-n</tt> de <tt>cat</tt>. Par exemple<6C>:
</p>
<pre>
<span class="prompt">galion ~ $</span><3E>cat bla
Pomme
Poire
Prune
<span class="prompt">galion ~ $</span><3E>cat -n bla &gt; blo
<span class="prompt">galion ~ $</span><3E>cat blo
1 Pomme
2 Poire
3 Prune
<span class="prompt">galion ~ $</span>
</pre></li>
</ol>
<h2><a name="jokers">Jokers et expressions r<>guli<6C>res</a></h2>
<ol>
<li> <strong> <a name="jokers1">Vous avez chez vous des fichiers appel<65>s
<tt>essai1</tt>, <tt>essai2</tt>, <tt>essai3</tt> et <tt>essai4</tt>. Comment
les effacer en une seule ligne de commande<64>? </a> </strong>
<p>
Il s'agit de donner le nom des fichiers en indiquant qu'ils commencent par
<tt>essai</tt> et qu'ils finissent par un chiffre. Il y a en fait plusieurs
fa<EFBFBD>ons de faire<72>:
</p>
<ul>
<li> <tt>rm essai[1234]</tt><3E>: les crochets signifient <20>l'un des caract<63>res
qui se trouvent entre crochets<74>;</li>
<li> <tt>rm essai[1-4]</tt><3E>: c'est le m<>me principe, sauf que l'on indique un
intervalle au lieu d'<27>crire explicitement les chiffres;</li>
<li> <tt>rm essai?</tt><3E>: le point d'interrogation d<>signe n'importe quel
caract<EFBFBD>re unique, le point y compris, sauf si le nom du fichier commence par
un point (<tt>ls<6C>?pine</tt> dans <tt>$HOME</tt> ne vous donnera pas, par
exemple, la liste des fichiers de configuration de Pine<6E>: <tt>.pinerc</tt>,
etc).
<p>
Ici, on trouvera tous les fichiers commen<65>ant par <tt>essai</tt>, suivi d'un
unique caract<63>re qui peut ne pas <20>tre un chiffre.</p>
</li>
<li> <tt>rm essai*</tt><3E>: c'est la formulation la plus vague<75>: effacer les
fichiers dont le nom commence par <tt>essai</tt> suivi de quelque chose,
c'est-<2D>-dire d'un nombre ind<6E>fini de caract<63>res, ou aucun.</li>
</ul></li>
<li> <strong> <a name="jokers2">Dans mon r<>pertoire d'accueil, j'ai un
certain nombre de fichiers avec un suffixe <tt>.c</tt>. Je d<>sire les
regrouper dans un r<>pertoire que j'appelerai <tt>C/</tt>. Quelles sont les
commandes que je dois taper<65>? </a> </strong>
<p>
On commande par cr<63>er un r<>pertoire <tt>C</tt> avec <tt>mkdir</tt>, puis, avec
<tt>mv</tt>, on d<>place tous les fichiers ayant un suffixe <tt>.</tt> et
dont le nom est fait d'une suite de caract<63>res quelconques<65>:
</p>
<pre>
<span class="prompt">galion ~ $</span><3E> ls
hello* zoinx*
hello.c zoinx.c
<span class="prompt">galion ~ $</span><3E> mkdir C
<span class="prompt">galion ~ $</span><3E> mv *.c C/
<span class="prompt">galion ~ $</span><3E> ls
C/ hello* zoinx*
<span class="prompt">galion ~ $</span><3E> ls C/
hello.c zoinx.c
</pre>
</li>
<li> <strong> <a name="jokers3">Vous d<>sirez regrouper dans un r<>pertoire
<tt>Rangement</tt> les fichiers dont le nom contient un caract<63>re minuscule
suivi d'un caract<63>re majuscule. Quelle(s) est/sont la/les commande(s) <20>
donner<EFBFBD>? </a> </strong>
<p>
On commence par cr<63>er le r<>pertoire <tt>Rangement</tt> avec <tt>mkdir</tt>.
Pour d<>signer les noms des fichiers, il faut indiquer la notion de
<EFBFBD>minuscule<EFBFBD>. On pourrait <20>crire explicitement l'alphabet entre crochets, pour
dire <20>l'un de ces caract<63>res<65>, puis faire de m<>me avec les majuscules. Mais on
gagne du temps en utilisant des intervalles (<tt>[a-z]</tt> et
<tt>[A-Z]</tt>). Le reste du nom du fichier, avant et apr<70>s la minuscule puis
la majuscule, est ind<6E>fini. On <20>crit donc<6E>:
</p>
<pre>
<span class="prompt">galion ~ $</span><3E>mkdir Rangement
<span class="prompt">galion ~ $</span><3E>mv *[a-z][A-Z]* Rangement/
</pre>
</li>
<li> <strong> <a name="jokers4">M<>me chose avec les fichiers dont le nom
contient trois voyelles <20> la suite. </a> </strong>
<p>
Le principe est le m<>me, sauf que l'on indique explicitement les voyelles
entre crochets<74>:
</p>
<pre>
<span class="prompt">galion ~ $</span><3E>mkdir Rangement
<span class="prompt">galion ~ $</span><3E>mv *[aeiou][aeiou][aeiou]* Rangement/
</pre>
</li>
<li> <strong> <a name="jokers5">En utilisant <tt>ls</tt> et <tt>grep</tt>,
affichez la liste des fichiers dans <tt>/bin</tt> dont le nom correspond <20> un
motif donn<6E>. </a>
</strong>
<p>
On peut proc<6F>der de deux fa<66>ons<6E>: utiliser <tt>ls</tt> seul et des jokers, ou
rediriger <tt>ls</tt> dans <tt>grep</tt> et utiliser les expressions
r<EFBFBD>guli<EFBFBD>res de <tt>grep</tt>.
</p>
<div class="attention">
<h1>&icone.attention;<3B>Attention<6F>&icone.attention;</h1>
<p>Dans la suite du corrig<69>, on suppose que l'on se trouve d<>j<EFBFBD> dans
<tt>/bin/</tt>.</p>
<p>On met des apostrophes autour des expressions de
<tt>grep</tt> pour les prot<6F>ger contre le shell. Enfin, on ne d<>taille pas les
expressions r<>guli<6C>res; <20> noter ces points importants <20> propos de
<tt>grep</tt><3E>:</p>
<ul>
<li> Le chapeau (<tt>^</tt>) en d<>but d'expression d<>signe le d<>but de la
ligne;</li>
<li> Le dollar en fin d'expression d<>signe la fin de la ligne;</li>
<li> Le point d<>signe n'importe quel caract<63>re; </li>
<li> L'<27>toile signifie <20>z<EFBFBD>ro ou plus de fois le caract<63>re qui pr<70>c<EFBFBD>de<64>; c'est
un multiplicateur;</li>
<li> Comme avec les jokers du shell, <tt>[^m]</tt> veut dire <20>sauf le
caract<EFBFBD>re m<>.</li>
</ul>
</div>
<p>
On constate que <tt>grep</tt> est plus complexe mais bien plus puissant que
les jokers du shell.
</p>
<table class="tableau">
<tr>
<td> </td>
<td> Avec <tt>ls</tt> seul </td>
<td> Avec <tt>ls</tt> et <tt>grep</tt> </td>
</tr>
<tr>
<td> Commence par <20>a<EFBFBD> et dont la deuxi<78>me lettre est <20>s<EFBFBD> ou <20>t<EFBFBD> </td>
<td> <tt> ls a[st]* </tt> </td>
<td> <tt> ls | grep '^a[st].*' </tt> </td>
</tr>
<tr>
<td> Contient <20>un<75> et se termine par <20>t<EFBFBD> </td>
<td> <tt>ls *un*t </tt> </td>
<td> <tt>ls | grep '.*un.*t$' </tt> </td>
</tr>
<tr>
<td> Contient <20>gre<72> ou <20>st<73> </td>
<td> <tt> ls *(gre|st)* </tt> </td>
<td> <tt> ls | grep '\(gre\|st\)' </tt> </td>
</tr>
<tr>
<td> Contient exactement deux lettres <20>m<EFBFBD> </td>
<td> </td>
<td> <tt>ls | grep '[^m]*m[^m]*m[^m]*' </tt> </td>
</tr>
<tr>
<td> Contient au moins deux lettres <20>m<EFBFBD> </td>
<td> </td>
<td> <tt>ls | grep '.*m.*m.*' </tt> </td>
</tr>
<tr>
<td> Contient au moins quatre caract<63>res et aucun chiffre </td>
<td> </td>
<td> <tt>ls | grep '^[^0-9]\{4,\}$' </tt> </td>
</tr>
<tr>
<td> Est constitu<74> de deux lettres exactement </td>
<td> <tt>ls<6C>?? </tt> </td>
<td> <tt>ls | grep '^..$' </tt> </td>
<td> </td>
</tr>
<tr>
<td> Commence et finit par un chiffre </td>
<td> <tt>ls [0-9]*[0-9] </tt> </td>
<td> <tt>ls | grep '^[0-9].*[0-9]$' </tt> </td>
</tr>
</table>
</li>
<li> <strong> <a name="jokers6"> Comment <20>liminer les lignes vides dans un
fichier<EFBFBD>? Comment <20>liminer les lignes ne contenant que des blancs<63>?
Comment <20>liminer toutes les lignes blanches<65>?</a> </strong>
<p class="continue">
<a name="precision">Une ligne vide</a> est diff<66>rente d'une ligne ne contenant
que des blancs, c'est-<2D>-dire des espaces ou des tabulations, m<>me si pour un
oeil humain cela revient au m<>me. La commande de recherche sera diff<66>rente
selon le cas<61>:
</p>
<ul>
<li> <strong>Lignes vides</strong><3E>: c'est <20>rien entre le d<>but et la fin de
la ligne<6E>; on <20>crit donc<6E>:
<pre>grep '^$' <em>fichier</em></pre></li>
<li> <strong>Lignes ne contenant que des blancs</strong><3E>: on utilise une
classe de caract<63>re pr<70>existante<74>: <tt>[:space:]</tt>. On <20>crit donc<6E>:
<pre>grep '^[[:space:]]$' <em>fichier</em></pre>
</li>
</ul>
<p>
Pour <20>liminer toutes les lignes blanches pour un oeil humain, on combine les
deux expressions et on utilise l'option <tt>-v</tt> qui inverse le sens de la
recherche. On n'a plus qu'<27> rediriger la sortie dans un fichier. On <20>crit
donc<EFBFBD>: </p>
<pre>grep -v '\(^[[:space:]]$\|^$\)' fichier1 &gt; fichier2 </pre>
<p class="continue">
en rempla<6C>ant <20>ventuellement le motif par <tt>'^$'</tt> ou
<tt>^[[:space:]]$'</tt> selon que l'on veut <20>ter les lignes vides ou les
lignes contenant des blancs.
</p></li>
</ol>
<h2><a name="germinal">Exercices sur <em>Germinal</em></a></h2>
<ol>
<li><a name="germinal1"> <strong>Combien de fois le mot <20>mine<6E> appara<72>t dans
chaque chapitre de la premi<6D>re partie de <em>Germinal</em><3E>?</strong></a> <p>
On cherche la cha<68>ne de caract<63>re <20>mine<6E>, qui soit un mot, <20>ventuellement
suivi d'un <20>s<EFBFBD>. On va donc d<>signer le d<>but du mot avec le symbole
<tt>\&lt;</tt>, la fin du mot avec le symbole <tt>\&gt;</tt>, et le <20>s<EFBFBD>
optionnel avec le point d'interrogation. On veut aussi faire appara<72>tre le
nombre d'occurences du mot, dans chaque fichier du r<>pertoire <tt>zola</tt>,
avec l'option <tt>-c</tt> de <tt>grep</tt>. </p>
<p>
Les noms des fichiers de <em>Germinal</em> sont de la forme
<tt>zola1.txt</tt>, de 1 <20> 6. Pour chercher dans tous les fichiers de cette
sorte, on peut <20>crire, au choix (attention <20> ne pas confondre les
<em>wildcards</em> du shell avec les expressions r<>guli<6C>res de grep).
</p>
<ul>
<li> <tt>zola*.txt</tt> (un ou plusieurs caract<63>res entre <tt>zola</tt> et
<tt>.txt</tt>).</li>
<li> <tt>*.txt</tt> (tous les fichiers <tt>.txt</tt> du r<>pertoire, mais il
pourrait y en avoir d'autres que <tt>zola*.txt</tt>).</li>
<li> <tt>zola?.txt</tt> (un seul caract<63>re entre <tt>zola</tt> et le
suffixe).</li>
<li> <tt>zola[1-6].txt</tt> (un chiffre entre 1 et 6 inclus entre
<tt>zola</tt> et le suffixe).</li>
</ul>
<p>
On peut donc <20>crire<72>:
</p>
<pre>grep -c '\&lt;mines?\&gt;' *.txt</pre>
<p>Et on obtient<6E>:</p>
<pre>
zola1.txt:5
zola2.txt:0
zola3.txt:3
zola4.txt:3
zola5.txt:1
zola6.txt:2
</pre>
<p>
Attention<EFBFBD>: les chiffres indiqu<71>s correspondent au nombre de lignes o<> figure
le mot <20>mine<6E>, mais pas au nombre r<>el d'occurences du mot. Pour un mot pr<70>cis
comme celui-ci, la marge d'erreur est faible, mais si vous cherchez un article
d<EFBFBD>fini, par exemple, cette m<>thode est insuffisante. C'est l<> qu'un langage
comme Perl est tr<74>s utile.
</p></li>
<li><a name="germinal2"> <strong>Combien y a-t-il de lignes blanches dans
<tt>zola1.txt</tt><3E>? </strong> </a> <p>
<a name="blanche"> Une ligne blanche, </a> c'est rien entre un d<>but de ligne
et une fin de ligne, ou alors une ligne ne contenant que des espaces et des
tabulations. On va donc chercher toutes les lignes correspondant <20> ce sch<63>ma
(voir <a href="#precision">plus haut</a> pour ces questions de d<>finitions).
Comme il y a deux motifs <20> chercher, on utilise l'expression r<>guli<6C>re qui
permet de chercher un motif ou un autre<72>: </p>
<pre>\(<em>motif1</em>\|<em>motif2</em>\)</pre>
<p>
La commande <20> taper est<73>:
</p>
<pre>grep '\(^[[:space:]]$\|^$\)' zola1.txt</pre>
<p>
Ensuite, on veut compter le nombre de lignes ainsi produites par
<tt>grep</tt>, avec la commande <tt>wc</tt> (<em>word count</em>); on va
utiliser l'option <tt>-l</tt> (<em>lines</em>, lignes). On tape donc<6E>:
</p>
<pre>grep '\(^[[:space:]]$\|^$\)' zola1.txt | wc -l</pre>
<p>
Et on obtient <20>81<38>. Bien <20>videmment, on aurait pu se rappeler que l'option
<tt>-c</tt> de <tt>grep</tt> donne le nombre de lignes matchant la cha<68>ne
recherch<EFBFBD>e, et obtenir le m<>me r<>sultat en ne lan<61>ant qu'un seul processus...
</p></li>
</ol>
<h2><a name="headtail"><tt>head</tt> et <tt>tail</tt></a></h2>
<!-- ####################################################<23>!-->
<ol>
<li> <strong> <a name="headtail1">Affichez les 15 premi<6D>res lignes du fichier
<tt>/etc/hosts</tt>, les 15 derni<6E>res lignes, toutes les lignes <20> partir de la
quinzi<EFBFBD>me, les lignes 15 <20> 20. </a> </strong> <p>
<strong><3E> propos du fichier <tt>/etc/hosts</tt></strong><3E>: les ordinateurs sur
l'Internet sont d<>sign<67>s par une adresse IP, constitu<74>e de 4 nombres entre 0
et 255 s<>par<61>s par des points. C'est cette adresse IP qui permet <20> un
ordinateur d'envoyer un message (datagramme IP) <20> un autre.
</p>
<p>
Cependant, m<>moriser les adresses IP n'est pas commode pour un humain. Pour
cette raison, les ordinateurs ont aussi un <20>nom<6F>, constitu<74> d'un nombre
variable de composantes s<>par<61>es par des points (par exemple,
<tt>research.att.com</tt>).
</p>
<p>
Le m<>canisme de conversion d'un nom en adresse IP (ou le m<>canisme inverse)
s'appelle la <20>r<EFBFBD>solution de noms<6D>. Elle se fait normalement par
l'interm<72>diaire de <em>nameservers</em>, ordinateurs sur lequel tourne le
programme BIND, qui se chargent de r<>pondre aux questions de r<>solution de
noms.
</p>
<p>
Cependant, pour certaines machines tr<74>s commun<75>ment acc<63>d<EFBFBD>es depuis un
ordinateur donn<6E> (par exemple, les machines de la m<>me salle) ou bien
pour <20>viter d'avoir <20> configurer un nameserver, on pr<70>f<EFBFBD>re parfois
utiliser le fichier <tt>/etc/hosts</tt><3E>: celui-ci d<>finit un certain nombre
de conversions nom -&gt; adresse IP qui seront consult<6C>es avant les
<em>nameservers</em>.</p>
<p>
La biblioth<74>que qui se charge de faire la traduction, et qui consulte d'abord
le <tt>/etc/hosts</tt> puis les <em>nameservers</em>, s'appelle la
<tt>libresolv</tt>.</p>
<ul>
<li> Afficher les 15 premi<6D>res lignes de <tt>/etc/hosts</tt><3E>:
<pre>head -15 /etc/hosts</pre></li>
<li> Afficher les 15 derni<6E>res lignes de <tt>/etc/hosts</tt><3E>:
<pre>tail -15 /etc/hosts</pre></li>
<li> Afficher toutes les lignes <20> partir de la quinzi<7A>me<6D>:
<pre>tail +15 /etc/hosts</pre></li>
<li> Affichez les lignes 15 <20> 20<32>:
<pre>head -20 /etc/hosts | tail -6 </pre></li>
</ul>
</li>
<li><a name="headtail2"> <strong>R<>cup<75>rer les lignes 5 <20> 9 d'un fichier de 12
lignes</strong> </a> <p>
Il y a deux solutions<6E>:
</p>
<ol>
<li> Prendre les neuf premi<6D>res lignes, et n'en garder que les cinq derni<6E>res
(de 5 <20> 9 inclus)<29>:
<pre>head -9 fichier | tail -5</pre>
</li>
<li> Prendre les 8 derni<6E>res lignes (de 5 <20> 12 inclus) et n'en garder que
les 5 premi<6D>res<65>:
<pre>tail -8 fichier | head -5</pre>
</li>
</ol></li>
<li> <strong> <a name="headtail4">Comment afficher la cinqui<75>me ligne d'un
fichier<EFBFBD>? </a> </strong>
<p>
On utilise la commande <tt>head</tt> pour extraire les cinq premi<6D>res
lignes du fichier, puis la commande <tt>tail</tt> pour ne conserver que la
derni<EFBFBD>re des cinq<6E>: </p>
<pre> head -5 fichier | tail -1 </pre>
</li>
</ol>
<h2><a name="syst">Filtres et redirections</a></h2>
<ol>
<li> <strong> <a name="syst1">Cr<43>ez un fichier dont chaque ligne commence
par un chiffre, suivi d'un slash (<tt>/</tt>), puis d'un ou plusieurs mots.
</a> </strong>
<p>
C'est la commande <tt>sort</tt> qui permet de trier des fichiers selon un
ordre donn<6E>.</p>
<ol>
<li> <strong>Affichez les lignes de ce fichier tri<72>es en ordre croissant,
suivant le nombre plac<61> en d<>but de ligne</strong> <p>
<tt>sort</tt> sans option trie automatiquement en fonction du premier caract<63>re,
par ordre alphab<61>tique si c'est une lettre, par ordre croissant si ce sont des
chiffres. Il suffit donc de taper<65>:
</p>
<pre>sort fichier</pre>
</li>
<li> <strong><3E>liminez de chaque ligne le chiffre et le caract<63>re
<EFBFBD><tt>/</tt><3E></strong>. <p> Pour cela, sans utiliser d'<27>diteur, on peut
utiliser la commande <tt>cut</tt>, qui <20>limine des champs dans une ligne. Par
d<EFBFBD>faut, le s<>parateur de champs est une tabulation. Ici, on peut demander <20>
<tt>cut</tt> de consid<69>rer le slash comme un s<>parateur de champs (option
<tt>-d</tt>, <20>d<EFBFBD>limiteur<75>), et de ne conserver que le deuxi<78>me champ du
fichier (option <tt>-f</tt>, <em>field</em>). On redirige ensuite la sortie
dans un autre fichier<65>: </p>
<pre> cut -d/ -f2 fichier &gt; fichier2</pre>
</li>
<li> <strong>Triez ces lignes par ordre alphab<61>tique inverse</strong>.
C'est l'option <tt>-r</tt> (<em>reverse</em>) de <tt>sort</tt> qui inverse le
r<EFBFBD>sultat de la comparaison. Par d<>faut, <tt>sort</tt> trie par ordre
alphab<EFBFBD>tique, il suffit donc de taper<65>:
<pre>sort -r fichier2</pre></li>
</ol>
</li>
<li><a name="syst2"> <strong>Combien de fichiers de configuration avez-vous
?</strong> </a>
<p>
La r<>ponse n'est pas simple, car vous avez <20>galement des r<>pertoires de
configuration, comme <tt>.netscape/</tt> qui contient entre autres vos
marque-pages (<tt>bookmarks.html</tt>), vos pr<70>f<EFBFBD>rences
(<tt>preferences</tt>), etc. On va compter le nombre de fichiers commen<65>ant
par un point situ<74>s dans votre r<>pertoire principal, en <20>liminant les copies
de sauvegarde (qui finissent par un tilde). Il y a plusieurs mani<6E>res de
proc<EFBFBD>der.</p>
<ol>
<li> <strong>ls, grep, tr, cut</strong> <p>
On peut penser <20> une solution complexe et bien lourde utilisant toutes ces
commandes, ce qui permet de les apprendre plus qu'autre chose... On affiche
une liste longue avec l'ensemble des fichiers (<tt>ls -la</tt>) puis qui
<EFBFBD>limine tous les r<>pertoires et les liens avec <tt>grep</tt> en ne gardant que
les lignes qui commencent par un tiret<65>: </p>
<pre>ls -al | grep '^-'</pre>
<p>
Ensuite, on utilise <tt>tr</tt> pour ne conserver qu'un seul espace entre
chaque champ de la liste, en utilisant l'option <tt>-s</tt> (<em>squeeze</em>)
qui remplace par une seule occurence la r<>p<EFBFBD>tition d'un caract<63>re sp<73>cifi<66>; on
met des apostrophes autour de l'espace pour qu'il soit compris comme tel et
non comme un simple espace<63>:
</p>
<pre>tr -s ' ' </pre>
<p>
On utilise <tt>cut</tt> pour conserver le dernier champ avec les noms de
fichiers (en l'occurence le neuvi<76>me champ), en indiquant que l'espace sert de
d<EFBFBD>limiteur de champ<6D>: </p>
<pre> cut -d' ' -f9 </pre>
<p>
Enfin, on demande <20> <tt>grep</tt> de ne retenir que les fichiers commen<65>ant
par un point, et ne finissant pas par un tilde, et on les comptel'option
<tt>-c</tt> de <tt>grep</tt>. Cela donne pour finir<69>: </p>
<pre> ls -al | grep '^-' | tr -s ' ' | cut -d' ' -f 9 | grep -c '^\..*[^~]$'
</pre>
<p>
Mais que de tourments pour si peu...<2E>:-)
</p></li>
<li> <strong>ls et grep </strong> <p> Il y a plus simple... On demande <20>
<tt>ls</tt> une liste des fichiers commen<65>ant par un point (avec l'option
<tt>-d</tt> pour ne pas descendre dans les r<>pertoires de configuration), on
ne garde que les lignes ne finissant pas par un <tt>/</tt> ou un tilde, et on
compte<EFBFBD>: </p>
<pre>ls -ad .* | grep -vc '\(~\|/\)$' </pre>
</li>
<li> <strong>find</strong>
<p>
On demande <20> <tt>find</tt> de chercher dans le r<>pertoire courant
(<tt>.</tt>), sans descendre dans l'arborescence (<tt>-maxdepth 1</tt>), tous
les fichiers normaux (<tt>-type f</tt><3E>: <em>file</em>), dont le nom commence
par un point (<tt>-name '.*'</tt>). On les compte ensuite avec <tt>wc</tt><3E>:
</p>
<pre>find . -maxdepth 1 -name '.*' -type f | wc -l</pre>
<p>
Cette solution est pr<70>f<EFBFBD>rable car elle ne lance que deux processus et non pas
au moins cinq comme la pr<70>c<EFBFBD>dente.
</p></li>
</ol>
</li>
<li><a name="syst3"> <strong>Combien de r<>pertoires de configuration avez-vous
?</strong> </a> <p>
Il y a deux solutions, l'une faisant appel <20> <tt>ls</tt> et <tt>grep</tt>,
l'autre faisant appel <20> <tt>find</tt>.
</p>
<ol>
<li> <strong><tt>ls</tt> et <tt>grep</tt></strong>
<p>
Le principe est le m<>me que dans l'exercice pr<70>c<EFBFBD>dent. La diff<66>rence est que
<tt>grep</tt> va chercher les lignes qui commencent par un point
<strong>et</strong> finissent par un slash. Cela se fait de la mani<6E>re
suivante<EFBFBD>: </p>
<pre>grep '^\..*/$'</pre>
<p>
<tt>.*</tt> signifie <20>n'importe quel caract<63>re (point) r<>p<EFBFBD>t<EFBFBD> z<>ro ou
plusieurs fois (<28>toile)<29>.
</p>
<p>
On tape donc<6E>: </p>
<pre>ls -ad .* | grep '^\..*/$' | wc -l</pre>
<p>
On part ici du principe que vous avez un alias de <tt>ls</tt> sur <tt>ls
-F</tt> (qui met un slash apr<70>s les noms de r<>pertoires, une <20>toile apr<70>s les
noms d'ex<65>cutables et une arobase apr<70>s les noms de liens). Si ce n'est pas le
cas, ajoutez-le dans un fichier <tt>.alias</tt> ou tapez <tt>ls -lF</tt>.
</p></li>
<li> <strong><tt>find</tt></strong>
<p>
On demande <20> <tt>find</tt> de chercher tous les fichiers de type <20>r<EFBFBD>pertoire<72>
(<tt>-type d</tt><3E>: <em>directory</em>), et dont le nom commence par un point
(<tt>-name '.*'</tt>, les quotes servant <20> prot<6F>ger du shell le contenu de
l'expression), dans descendre dans l'arborescence.
</p>
<p>
On tape donc<6E>: </p>
<pre>find . -maxdepth 1 type d -name '.*' | wc -l</pre>
</li>
</ol>
<p>
Il y a une inexactitude dans le r<>sultat<61>: <tt>find</tt> compte aussi
<tt>./</tt> dans le r<>sultat.
</p></li>
<li> <strong> <a name="syst4">Comment mettre dans un fichier la liste de
tous les fichiers de l'arborescence <20> partir du r<>pertoire courant<6E>?
</a> </strong> <p>
C'est l'option <tt>-R</tt> de <tt>ls</tt> qui permet d'afficher
r<EFBFBD>cursivement le contenu d'un r<>pertoire et de ses sous-r<>pertoires (<28> ne
pas confondre avec <tt>-r</tt> qui inverse le tri). Pour rediriger la
sortie d'une commande dans un fichier, on utilise le caract<63>re
<tt>&gt;</tt>. Pour mettre la liste de tous les fichiers de l'arborescence
dans un fichier <tt>toto</tt>, il faut donc taper<65>: </p>
<pre> ls -R &gt; toto </pre>
</li>
<li><a name="syst5"> <strong>Cr<43>ez un fichier <tt>liste</tt> contenant la
liste de tous vos fichiers, avec leur taille, leurs droits, etc</strong></a>
<p>
Il va s'agir de faire une liste r<>cursive de l'ensemble de votre compte
(option <tt>-R</tt> de <tt>ls</tt>), incluant les fichiers de configuration
(option <tt>-a</tt>) et les renseignements donn<6E>s par l'option <tt>-l</tt>.
</p>
<p>
De la longue liste obtenue, on ne veut retenir que les fichiers, dont les
droits commencent par un tiret (un <tt>d</tt> identifie les r<>pertoires). On
va donc demander <20> <tt>grep</tt> de chercher toutes les lignes commen<65>ant par
un tiret.
</p>
<p>
Enfin, on va placer le r<>sultat dans un fichier appel<65> <tt>liste</tt>. On tape
donc<EFBFBD>: </p>
<pre>ls -alR | grep '^-' &gt; liste</pre>
</li>
<li> <strong> <a name="syst6">Comment afficher uniquement les fichiers
du r<>pertoire courant qui sont des liens symboliques<65>? </a> </strong>
<p>
L'option <tt>-l</tt> de <tt>ls</tt> donne une liste longue des fichiers,
incluant les droits et le type des fichiers, identifi<66>s par une
lettre<EFBFBD>: rien pour un fichier, <20>d<EFBFBD> pour un r<>pertoire
(<em>directory</em>), <20>l<EFBFBD> pour un lien (<em>link</em>); par exemple<6C>:
</p>
<pre>
drwxr-xr-x 2 emilia users 1024 mai 10 02:27 www/
</pre>
<p>
Pour obtenir la liste des fichiers du r<>pertoire courant qui sont des liens
symboliques, il suffit donc d'afficher cette liste longue et de ne retenir
que les lignes qui commencent par un <20>l<EFBFBD>, gr<67>ce <20> la commande
<tt>grep</tt>. On utilise une expression r<>guli<6C>re pour cela<6C>: le
chapeau (<tt>^</tt>) d<>signe le d<>but de la ligne, et l'ensemble de
l'expression est plac<61>e entre apostrophes pour qu'elle ne soit pas
interpr<EFBFBD>t<EFBFBD>e par le shell. On tape donc<6E>:
</p>
<pre>ls -l | grep '^l' </pre>
</li>
<li><a name="syst7"> <strong>Combien de lignes contiennent le mot
<EFBFBD><em>file</em><3E> dans la page de man de <tt>less</tt><3E>? </strong> </a> <p>
Il faut lancer le man de <tt>less</tt>, et ensuite chercher dedans le nombre
de lignes contenant le mot <tt>file</tt> (<28>fichier<65> en anglais), avec
<tt>grep</tt>. C'est l'option <tt>-c</tt> (<em>count</em>) de <tt>grep</tt>
qui va servir.
</p>
<p>
Finalement, on <20>crit<69>: </p>
<pre>man less | grep -c file</pre>
<p>
Et on obtient 205. C'est le nombre de lignes contenant au moins une fois la
cha<EFBFBD>ne de caract<63>res <em>file</em>, mais pas le nombre d'occurences du mot
dans le fichier<65>: il pourrait y avoir plusieurs fois <em>file</em> sur la m<>me
ligne. On atteint l<> encore la limite de ce que sait faire <tt>grep</tt>.
</p></li>
<li><a name="syst8"> <strong>Quels sont les dix plus gros fichiers de
<tt>/usr/bin/</tt><3E>?</strong> </a>
<p>
Il faut d'abord faire une liste des fichiers avec leurs tailles (option
<tt>-l</tt> de <tt>ls</tt>. Ensuite, il faut trier ces lignes en fonction de
la taille, avec la commande <tt>sort</tt>. Une ligne est de la forme
suivante<EFBFBD>:
</p>
<pre>
-rwxr-xr-x 1 root staff 5872 Jan 21 1994 /usr/bin/rm*
</pre>
<p>
C'est le cinqui<75>me champ, celui de la taille, qui nous int<6E>resse. La
num<EFBFBD>rotation commen<65>ant <20> 0 (comme les <20>tages), pour indiquer le cinqui<75>me
champ on <20>crit <tt>+4</tt>. Ensuite, on veut que le tri se fasse selon un
crit<EFBFBD>re num<75>rique (option <tt>-n</tt>) et <20> l'envers, en commen<65>ant par les
plus grandes valeurs (option <tt>-r</tt>, <em>reverse</em>).
</p>
<p>
Pour finir, on veut les dix premi<6D>res lignes du r<>sultat. On les s<>lectionne
avec la commande <tt>head</tt>; par d<>faut, elle prend les dix premi<6D>res
lignes d'un fichier, il n'est donc pas n<>cessaire de pr<70>ciser le nombre de
lignes voulues. Finalement, on <20>crit<69>:
</p>
<pre>ls -l /usr/bin | sort -nr +4 | head</pre>
</li>
<li> <strong> <a name="syst9">Pour chaque ligne du fichier
<tt>/etc/hosts</tt>, affichez<65>: </a> </strong>
<ul>
<li> Le cinqui<75>me caract<63>re;</li>
<li> Les caract<63>res 5 <20> 10, et le treizi<7A>me;</li>
<li> Tous les caract<63>res <20> partir du quinzi<7A>me.</li>
</ul>
<p>Une mani<6E>re de r<>soudre cet exercice consiste <20> utiliser le programme
<code>sed</code> qui sert <20> manipuler du texte.</p>
<p>Nous allons utiliser la commande <code>s</code> de <code>sed</code>.
Sa syntaxe est la</p> <pre>s/<em>regexp</em>/<em>remplacement</em>/</pre> <p
class="continue">o<> <em>regexp</em> est une <a
href="&url.tuteurs;unix/exercices/grep.html">expression r<>guli<6C>re</a> et
<em>remplacement</em> un cha<68>ne devant la remplacer.</p>
<ul>
<li>
Pour afficher le cinqui<75>me caract<63>re de chaque ligne, on peut faire
ceci<EFBFBD>:
<pre>
<span class="prompt">clipper ~ $</span><3E>cat /etc/hosts | sed -e "s/^.\{4\}\(.\).*/\1/"
</pre>
<p class="continue">Le <code>"^.\{4\}"</code> du d<>but sert <20> filtrer les
4 premiers caract<63>res de chaque ligne. Ensuite, <code>\(.\)</code> filtre
n'importe quel caract<63>re, le fait de mettre des parenth<74>ses enregistre le
caract<EFBFBD>re en question dans une variable que l'on peut utiliser dans
la cha<68>ne de remplacement avec <code>"\1"</code>. Ensuite,
<code>.*</code> filtre le reste de la ligne.</p></li>
<li>
Sur le m<>me principe, pour afficher les caract<63>res 5 <20> 10 et le treizi<7A>me :
<pre>
<span class="prompt">clipper ~ $</span><3E>cat /etc/hosts | sed -e "s/^.\{4\}\(.\{6\}\)..\(.\).*/\1 \2/"
</pre>
</li>
<li>
Et enfin, pour afficher tous les caract<63>res <20> partir du quinzi<7A>me<6D>:
<pre>
<span class="prompt">clipper ~ $</span><3E>cat /etc/hosts | sed -e "s/^.\{14\}//"
</pre>
</li>
</ul>
</li>
</ol>
<h2><a name="yp">Exercices sur les pages jaunes</a></h2>
<!-- ####################################################<23>!-->
<p>
Les pages jaunes (<em>yellow pages</em>) sont un syst<73>me qui centralise
certaines bases de donn<6E>es communes <20> toutes les machines du syst<73>me (logins
et noms utilisateurs, mots de passes, groupes d'utilisateurs, noms et adresses
des autres machines sur le r<>seau) sur un petit nombre de serveurs redondants;
cela permet de garder des bases de donn<6E>es coh<6F>rentes, plus facilement que si
on devait mettre <20> jour leur contenu sur plusieurs centaines de machines <20> la
fois.
</p>
<p>
On dit plut<75>t aujourd'hui NIS ou Network Information System, Yellow Pages
<EFBFBD>tant une marque d<>pos<6F>e de British Telecom.</p>
<p>
La commande <tt>ypcat</tt> permet de visualiser une base de donn<6E>es: citons en
particulier: <tt>ypcat passwd</tt>, <tt>ypcat group</tt>, <tt>ypcat
hosts</tt>.</p>
<p>
Ces bases de donn<6E>es sont les versions centralis<69>es des fichiers
<tt>/etc/passwd</tt>, <tt>/etc/group</tt> et <tt>/etc/hosts</tt>, lesquels ne
contiennent alors plus que quelques informations; par exemple, le fichier
<tt>/etc/hosts</tt> contient tout de m<>me des adresses de machines, ne
serait-ce que celle du serveur de pages jaunes....
</p>
<ol>
<li><a name="yp2"> <strong>Combien de personnes de la promotion 1996 ont un
login commen<65>ant par <20>l<EFBFBD><6C>? </strong> </a> <p>
Pour avoir une liste de tous les comptes, on tape </p>
<pre>ypcat passwd</pre>
<p>
On veut ensuite s<>lectionner les gens dont le r<>pertoire personnel contient
<tt>/96/</tt>, et dont le login commence par <tt>l</tt>, et compter le nombre
de lignes (option <tt>-c</tt> de <tt>grep</tt>). On tape donc<6E>: </p>
<pre>ypcat passwd |grep c '^l.*/96/'</pre>
<p>
et on obtient 19.
</p>
</li>
<li><a name="yp3"> <strong>Est-ce que des gens des promotions de la d<>cennie
1980 ont encore un compte sur clipper<65>? </strong> </a> <p>
On veut savoir si des chemins d'acc<63>s de r<>pertoires personnels sont de la
forme </p>
<pre>/users/8[0-9]</pre>
<p>
On tape donc<6E>:
</p>
<pre>ypcat passwd | grep '/users/8[0-9]'</pre></li>
<li><a name="yp4"> <strong>Combien y a-t-il de comptes d'<27>l<EFBFBD>ves, magist<73>riens,
etc (appartenant <20> une promotion) sur clipper<65>? </strong> </a> <p>
On veut avoir une liste des gens dont le chemin d'acc<63>s du r<>pertoire
personnel commence par <tt>/users/</tt> et un num<75>ro<72>: <tt>/users/9[0-9]</tt>
(puisqu'on vient de voir qu'il n'y a pas de gens des promotions ant<6E>rieures).
On utilise l'option <tt>-c</tt> (<em>count</em>) pour avoir le nombre de
personnes<EFBFBD>: </p>
<pre>ypcat passwd | grep -c '/users/9[0-9]/'</pre>
</li>
<li><a name="yp5"> <strong>Classer les comptes de ces utilisateurs par num<75>ro
d'UID</strong> </a> <p>
Le num<75>ro d'ID (<em>user identification</em>) identifie les utilisateurs, les
num<EFBFBD>ros les plus bas correspondant au syst<73>me (l'UID de root est 0). C'est le
troisi<EFBFBD>me champ, les champs <20>tant s<>par<61>s par des deux-points.
</p>
<p>
On va utiliser <tt>sort</tt> pour trier, selon un crit<69>re num<75>rique (option
<tt>-n</tt>). Par d<>faut, avec <tt>sort</tt> les champs sont d<>limit<69>s par des
blancs, il faut donc indiquer qu'ici ce sont des deux-points, avec l'option
<tt>-t</tt>. Enfin, on trie en fonction du troisi<73>me champ (<tt>2</tt>,
la num<75>rotation commen<65>ant <20> 0). </p>
<p>
On suppose aussi que l'on veut pouvoir lire le r<>sultat du tri avec
<tt>less</tt>. On tape donc<6E>: </p>
<pre>ypcat passwd | grep '/users/9[0-9]' | sort -n -t: +2 | less</pre>
</li>
<li> <strong> <a name="yp6">Affichez le login des personnes ayant
<tt>zsh</tt> comme shell de login.
</a> </strong>
On utilise <code>grep</code> pour ne garder que les lignes des pages
jaunes correspondant aux personnes ayant <code>zsh</code> comme shell de
login et on utilise <code>sed</code> pour n'afficher que le login<69>:
<pre>
<span class="prompt">bireme ~ $</span><3E>ypcat passwd | grep "zsh$" | sed -e "s/:.*//"
</pre>
<div class="ens">
Comme vous pourrez le constater, sur <code>clipper</code>, tout le
monde <20> <code>/bin/sh</code> comme shell de login. Le shell (en g<>n<EFBFBD>ral
<code>zsh</code>) n'est lanc<6E> qu'<27> partir du script
<code>.profile</code>. Il s'agit d'un choix de l'administrateur syst<73>me.
</div>
</li>
<li> <strong> <a name="yp7">Affichez le nom et le pr<70>nom des personnes
ayant un compte sur <tt>clipper</tt>, et dont le nom de famille contient au
moins 2 voyelles c<>te <20> c<>te. Compter ces personnes. </a>
</strong>
<pre>
<span class="prompt">bireme ~ $</span><3E>ypcat passwd | sed -e "s/:\/.*//
s/.*://" | grep " .*[aeiou][aeiou].*"
</pre>
</li>
</ol>
<h2><a name="divers">Divers</a></h2>
<ol>
<li> <strong> <a name="divers1">Comment conserver les informations concernant
le disque dans un fichier<65>? </a> </strong>
<p>
C'est la commande <tt>df</tt> qui donne des informations sur le disque et sur
les partitions mont<6E>es. Pour rediriger la sortie d'une commande dans un
fichier, on utilise l'op<6F>rateur de redirection <tt>&gt;</tt> suivi du nom du
fichier. Dans notre cas, pour conserver les informations dans un fichier
appel<EFBFBD> <tt>disque</tt>, on <20>crira<72>:
</p>
<pre>
df &gt; disque
</pre></li>
<li>
<strong> <a name="divers3">Comment conna<6E>tre la derni<6E>re date de connexion au
syst<EFBFBD>me d'un utilisateur de login <tt>martin</tt><3E>? </a> </strong>
<p>
C'est la commande <tt>finger</tt> qui donne des renseignements sur les
utilisateurs. Si on tape
</p>
<pre>
finger martin
</pre>
<p class="continue">
ou mieux (plus rapide),
</p>
<pre>
finger -m martin
</pre>
<p class="continue">
les renseignements concernant l'utilisateur s'affichent. On y trouve la
date de son dernier login sur la machine o<> vous <20>tes, ainsi que la date de la
derni<EFBFBD>re fois o<> il a lu son courrier (mais ce <20> condition qu'il ait re<72>u du
courrier depuis...).
</p></li>
<!-- <p>
<li><strong> <a name="divers4"> Cr<43>ez chez vous un fichier contenant des mots
(sur le mod<6F>le d'un carnet de vocabulaire), un par ligne, avec des
r<EFBFBD>p<EFBFBD>titions. </a></strong> <br>
Utilisez la commande <tt>uniq</tt><3E>:
<ul>
<li> Pour <20>liminer les lignes redondantes (dans notre exemple, la ligne
<EFBFBD>Pomme<EFBFBD> en double exemplaire);
<li> Pour donner le nombre d'occurrences de chaque ligne;
<li> Pour ne faire appara<72>tre que le ou les mots redondants avec le nombre
d'occurrences; ici, ce sera<72>: <br>
<tt> 2 Pomme </tt>
</ul>
!-->
<li><strong> <a name="divers5"> Affichez le type des fichiers <tt>/etc/passwd</tt>,
<tt>/usr/include/stdio.h</tt>, <tt>/bin/ls</tt> et de votre r<>pertoire de
travail. </a></strong>
<p>
C'est la commande <tt>file</tt> qui permet d'afficher le type des fichiers.
Dans notre cas, il suffit d'<27>crire:
</p>
<pre>
<span class="prompt">galion ~ $</span><3E>file /etc/passwd /usr/include/stdio.h /bin/ls .
/etc/passwd: ASCII text
/usr/include/stdio.h: C program text
/bin/ls: ELF 32-bit MSB executable, SPARC, version 1, stripped
.: directory
<span class="prompt">galion ~ $</span>
</pre>
</li>
</ol>
<div class="metainformation">
Auteur<75>: Anonyme, Jo<4A>l Riou. Derni<6E>re modification le 2002-12-09.
</div>
</body>
</html>