\typeout{+==============================================+}
\typeout{|============= LA BOITE A OUTILS ==============|}
\typeout{+==============================================+}
\chapter{La boîte à outils}
\label{chap-toolbox}
\begin{epigraphe}{{\ttfamily\upshape man cut}}
  Cut prints selected  parts of lines\\
  from each FILE to standard output.\\
  With no FILE, or when FILE is -,\\
  read standard input.
\end{epigraphe}

\begin{introchap}{L'idée}
  conductrice des développeurs d'\unix{} de la première heure était de
  concevoir des outils élémentaires destinés à une tâche bien
  spécifique ; chacun de ces outils devait être conçu de telle manière
  qu'il puisse communiquer avec les autres. En tant
  qu'utilisateur d'\unix{} on retrouve cette philosophie à tout moment
  : chaque utilitaire, à l'instar d'une pièce de meccano\registered{},
  est destiné à effectuer une tâche simple et la connaissance de
  quelques pièces permet de construire ses propres outils. Ce chapitre
  a pour objet de présenter les mécanismes qui permettent de faire
  communiquer ces programmes entre eux, puis de présenter les pièces
  de la boîte à outils les plus communément utilisées sous \unix{}.
\end{introchap}


\section{Introduction à l'expansion}
\label{sec-unix-expansion}
\index{commande!expansion}
\index{expansion}
\index{;@\texttt{*}}
\index{;@\texttt{\textbackslash}}
Avant d'aborder l'étude de quelques outils \unix{}, il convient de
comprendre quel\-ques-uns des mécanismes entrant en jeu dans
l'interprétation des commandes. Comme nous l'avons vu précédemment,
certains \voir{sec-unix-car}{caractères} ont une signification
particulière pour le shell. Lorsqu'on veut utiliser ces caractères
autrement que pour leur signification spéciale, il faut généralement
utiliser le caractère \anglais{backslash} (\verb+\+) :
\begin{shellcom}
  ¢usercom«echo \*»
  *
\end{shellcom}
Sans le backslash, le caractère  \verb+*+ aurait été remplacé par tous
les fichiers du répertoire courant. Un moyen couramment utilisé avec
le shell pour protéger les caractères spéciaux est d'utiliser les
caractères \emph{quote} \verb+'+ et \emph{double quote} \verb+"+. Le
premier évite l'expansion de tout caractère :
\begin{shellcom}
  ¢usercom«echo '$USER #'»
  $USER #
\end{shellcom}
\index{;@\texttt{\$}}%
Le deuxième permet au caractère \verb+$+ de
conserver sa signification :
\begin{shellcom}
  ¢usercom«echo "$USER #"»
  lozano #
\end{shellcom}
%$
Il sera souvent question de protéger les caractères spéciaux lors de
l'utilisation des outils que nous présentons dans ce chapitre.

\section{Redirections et tubes}
\label{sec-pipe} 
\index{redirections|(}


Par défaut, tout processus a accès à trois canaux de communications
(figure~\ref{fig-flux}):
\begin{figure}[htbp]
  \begin{center}
    \includegraphics[width=4cm]{flux}
    \caption{Les trois flux associés à un processus}
    \label{fig-flux}
  \end{center}
\end{figure}
\begin{enumerate}
\index{standard!output}
\index{standard!input}
\index{standard!error}
\index{erreur standard}
\index{sortie standard}
\index{entrée standard}
\item le flux de \emph{sortie} (\anglais{standard output}) par défaut
  dirigé vers le terminal courant ;
\item le flux d'\emph{erreur} (\anglais{standard error}) également
  dirigé par défaut vers le terminal courant ;
\item le flux d'\emph{entrée} (\anglais{standard input}) sur lequel
  transitent par défaut les données provenant du clavier.
\end{enumerate}
Ainsi, la commande :
\begin{shellcom}
  ¢usercom«echo bonjour»
  bonjour
\end{shellcom}
affiche « bonjour » sur le flux de sortie. La commande :
\begin{shellcom}
  ¢usercom«ls amlkjg»
  ls: amlkj: No such file or directory
\end{shellcom}
affiche le message sur le flux d'erreur. Et la commande (du shell
\texttt{bash}) :
\begin{shellcom}
  ¢usercom«read A»
  hop là¢comcomment«saisie par l'utilisateur»
\end{shellcom}
attend la saisie d'une ligne sur le flux d'entrée, ligne qui sera
stockée dans la variable \texttt{A}.

Ce qu'il est intéressant de comprendre, c'est que la grande majorité
des commandes et utilitaires d'\unix{} suivent le modèle de ces trois
flux standard, qu'on pourrait définir de la manière suivante :
\begin{itemize}
\item tout affichage est envoyé par défaut sur le flux de sortie ;
\item tout message d'erreur est envoyé sur le flux d'erreur ;
\item toute donnée peut être lue depuis le flux d'entrée.
\end{itemize}
\`A titre d'exemple, la commande \com{grep} suit ce modèle. Cette
commande est destinée à ne récupérer que les lignes d'un fichier
contenant une \voir[][5pt]{sec-regexp}{chaîne de caractères} :
\begin{enumerate}
\item \com{grep} affiche le résultat sur le flux de sortie ;
\item les messages d'erreurs éventuels sont affichés sur le flux d'erreur ;
\item \com{grep} peut attendre les données à filtrer sur son flux d'entrée :
  \begin{shellcom}[noendprompt]
    ¢usercom«grep» bof ¢comcomment«on recherche la chaîne ¢texttt«bof»»
    ¢usercomwop«xxxbofyyy» ¢touche«Entrée»¢comcomment«saisie par l'utilisateur»
    xxxbofyyy ¢comcomment«correspondance : ¢com«grep» affiche la ligne»
    ¢usercomwop«xxxbifyyy» ¢touche«Entrée»¢comcomment«saisie par l'utilisateur»
    ¢comcomment«pas de correspondance»
  \end{shellcom}
\end{enumerate}
On peut indiquer à une commande qui attend des données sur son flux
d'entrée, la fin des données à l'aide du caractère \texttt{EOF} saisi
par la combinaison de touches \toucheCtrl{}\touche{d}\hspace*{-.2em}.
\index{caractères!de fin de fichier}
\index{EOF}
\index{Ctrl-d (fin de fichier)}

Nous verrons par la suite que c'est précisément parce que la plupart
des commandes \unix{} suit ce modèle des trois flux, qu'on peut
\emph{composer} les utilitaires avec une grande souplesse. Cette «
composabilité » qui fait la grande force d'\unix{} est possible grâce
à ce qu'on appelle les \emph{redirections}.

\subsection{Redirections}
\label{sec-redirection}

Il est possible de \emph{rediriger} les trois flux présentés au
paragraphe précédent, grâce à une syntaxe particulière qui varie très
légèrement selon
le shell utilisé.


\subsubsection{Redirection du flux de sortie}

\index{;@\texttt{<}}
\index{redirection!du flux de sortie}

On peut rediriger le flux de sortie vers un fichier :
\begin{shellcom}
  ¢usercom«echo bonjour > test.txt»
  ¢usercom«ls -l test.txt»
  -rw-r--r--   1 vincent  users 8 Dec 11 16:19 test.txt
\end{shellcom}
Le caractère \texttt{>} permet la redirection dans le fichier
\texttt{test.txt}. On peut noter que le choix du caractère évoque la
direction du flux « vers » le fichier. Puis :
\begin{shellcom}
  ¢usercom«more test.txt»
  bonjour
\end{shellcom}
La \voir{sec-toolbox-afficher}{commande \com{more}} qui a pour but
d'afficher le contenu d'un fichier à l'écran, permet ici de comprendre
que \com{echo} a réalisé son affichage dans le fichier
\texttt{test.txt} et non à l'écran.

\subsubsection{Redirection du flux d'erreur}
\index{;@\texttt{2>}}
\index{redirections!du flux d'erreur}

On peut dans certaines situations vouloir récupérer les informations
envoyées par un programme sur le flux d'erreur. Le principe est le
même que pour le flux de sortie, seul l'opérateur est différent :
\begin{shellcom}
  ¢usercom«ls qsmlkjf 2> erreur.txt»
  ¢usercom«cat erreur.txt»
  ls: qsmlkjf: No such file or directory
\end{shellcom}
On peut également coupler les deux redirections de la manière suivante
:
\begin{comsyntax}
  unprogramme > sortie.txt 2> erreur.txt
\end{comsyntax}
Pour rediriger les deux flux (sortie et erreur) dans un même fichier,
on pourra utiliser :
\begin{comsyntax}
  unprogramme > sortie-erreur 2>\&1
\end{comsyntax}
Ou plus simple :
\begin{comsyntax}
  unprogramme >\& sortie-erreur
\end{comsyntax}


\subsubsection{Redirection en mode ajout}
\index{;@\texttt{>{}>}}

L'opérateur \texttt{>} crée ou écrase le fichier destinataire de la
redirection. On peut cependant utiliser le mode « ajout »
(\anglais{append}) pour ne pas écraser un fichier déjà existant :
\begin{shellcom}
  ¢usercom«echo bonjour > donnees.txt»
  ¢usercom«echo salut >«»> donnees.txt»
  ¢usercom«cat donnees.txt»
  bonjour
  salut
\end{shellcom}



\subsubsection{Redirection du flux d'entrée}
\index{redirections!du flux d'entrée}

Lorsqu'un programme attend des données depuis le clavier, on peut lui
fournir directement ces données en utilisant le contenu d'un fichier.
On dit alors qu'on redirige le flux d'entrée du programme en question.
Le premier exemple que nous proposons utilise la commande \com{bc}
qui est d'après la page de manuel, \emph{an arbitrary precision
  calculator language.} Une session avec \com{bc} se présente comme
suit :
\begin{shellcom}
  ¢usercom«bc -q»
  ¢comans«2+3*5» ¢touche«Entrée»
  17
  ¢comans«quit»
\end{shellcom}
Si on stocke l'expression du calcul (\texttt{2+3*5}) dans un fichier,
on peut indiquer à \com{bc} que les données proviennent de ce fichier
:
\begin{shellcom}
  ¢usercom«cat calcul.dat»
  2+3*5
  ¢usercom«bc < calcul.dat»
  17
\end{shellcom}
On dit qu'on a redirigé le flux d'entrée de \com{bc} depuis le fichier
\texttt{calcul.dat}.


Le second exemple concerne la commande \com{mail}. Pour envoyer un
mail en utilisant cette commande, le principe est le suivant :
\begin{shellcom}
  ¢usercom«mail -s "Des redirections" monpote»
  ¢usercomwop«Bon ben alors...»
  ¢usercomwop«A+»
  ¢usercomwop«.»
  EOT
\end{shellcom}
l'option \texttt{-s} permet de spécifier le sujet. L'argument qui suit
(\texttt{monpote}) est l'adres\-se électronique du destinataire du mail.
Une fois la commande lancée, on peut taper le texte du message ; le
caractère~«.» sur une seule ligne permet de finir le texte et
d'envoyer le message.
\begin{gnuposix}
  La version originelle de \com{mail} ne dispose pas de l'option
  \texttt{-s}, cette option apparaît dans le programme standard
  \com{mailx}. Sur le système \linux{} de votre serviteur \com{mailx}
  et \com{mail} font référence au même programme. Attention par contre, d'autres
  systèmes peuvent être configurés différemment.
\end{gnuposix}

Ce qu'il faut comprendre ici, c'est que \com{mail} attend le texte du
message sur le flux d'entrée. On peut alors rediriger cette entrée «
depuis » un fichier qui contiendrait le corps du message :
\begin{shellcom}
  ¢usercom«cat msg.txt  ¢comcomment«¢texttt«msg.txt» contient le message»»
  alors voilà
  A+
  ¢usercom«mail -s "redirections" monpote < msg.txt»
\end{shellcom}
Donc en supposant que l'on ait saisi le message dans le fichier
\texttt{msg.txt}, la dernière commande \com{mail} peut se traduire par
« envoyer un mail en lisant les données depuis le fichier
\texttt{msg.txt}. » On dit alors qu'on a redirigé le flux d'entrée.


\subsubsection{Le trou noir}
\index{devnull@\texttt{/dev/null}} \index{trou noir} On trouve
généralement sur les systèmes \unix{} un fichier particulier dont le
nom est \texttt{/dev/null} : c'est un « trou noir » qui absorbe toute
redirection sans broncher ; ce fichier est un fichier spécial, il ne
grossit pas lorsqu'on y redirige des données. Il peut être utile
pour ne pas être dérangé par les messages d'erreur par exemple :
\begin{comsyntax}
  id \comarg{user} > test 2> /dev/null
\end{comsyntax}
cette commande écrit dans le fichier \texttt{test} les numéros
identifiant l'utilisateur \comarg{user}. Si l'utilisateur n'existe pas
le message d'erreur n'est pas affiché puisqu'envoyé dans
\texttt{/dev/null}. 

\subsection{Les tubes (\anglais{pipes})}
\label{sec-pipes}
\index{redirections!tubes|(}
\index{tubes|(}

Les tubes ou \anglais{pipes} offrent un mécanisme permettant de
composer plusieurs commandes en connectant le flux de sortie de l'une
avec le flux d'entrée de la suivante (figure~\ref{fig-pipes}). 
\begin{figure}[htbp]
  \begin{center}
    \includegraphics[width=\textwidth]{pipes}
    \caption{Principe des tubes}
    \label{fig-pipes}
  \end{center}
\end{figure}

Reprenons l'exemple de l'envoi du mail du paragraphe précédent.
Supposons que nous disposions de deux fichiers de données
contenant :\framaallongepage{1}
\begin{shellcom}
  ¢usercom«more data-I.txt»
  machin
  chose
\end{shellcom}
et :
\begin{shellcom}
  ¢usercom«more data-II.txt»
  bidule
  chouette
\end{shellcom}
\index{;@\texttt{"|}}%
On veut envoyer ces deux fichiers au sieur
\texttt{monpote}. Pour ce faire :
\begin{shellcom}
  ¢usercom«cat data-I.txt data-II.txt | mail -s "some data" monpote»
\end{shellcom}
La \emph{sortie} de la commande \com{cat} ---~qui réalise la
concaténation des données des fichiers \texttt{data-I.txt} et
\texttt{data-II.txt}~--- est utilisée comme \emph{entrée} de la commande
\com{mail} (corps du message). On peut composer à l'« infini » les
tubes comme le montre l'exemple suivant : on veut également envoyer
une copie des données triées au gars \texttt{monpote}. Le tri peut
être fait par la commande \voir[][-12pt]{sec-sort}{\com{sort}} :
\begin{shellcom}
  ¢usercom«sort data-I.txt data-II.txt»
  bidule
  chose
  chouette
  machin
\end{shellcom}
On peut donc exploiter le fait que \com{sort} peut trier les données
arrivant sur le flux d'entrée, pour envoyer le mail avec \emph{deux}
tubes :
\begin{shellcom}
  ¢usercom«cat data-I.txt data-II.txt | sort | mail -s "some data" monpote»
\end{shellcom}
donc ici, la sortie de \com{cat} est utilisée comme entrée pour
\com{sort} qui envoie à \com{mail} le corps du message sur le flux
d'entrée.   Notez enfin qu'on aurait pu écrire :
\begin{shellcom}
  ¢usercom«sort data-I.txt data-II.txt | mail -s "some data" monpote»
\end{shellcom}
car \com{sort} est capable de trier les fichiers qu'on lui passe en
arguments. L'exemple avait ici pour but de combiner plusieurs tubes.
\begin{attention}
  Un point important à noter est que lors de l'utilisation de
  plusieurs commandes par l'intermédiaire de tubes, chacune des
  commandes est lancée \emph{en même temps} par le système. Bien que
  l'on imagine visuellement un flux d'information transitant de gauche
  à droite, les commandes sont lancées de manière concurrente.
\end{attention}
Toute utilisation « sérieuse » du shell d'\unix{} passe par une
utilisation fréquente des tubes et redirections. Les utilitaires
présentés dans la suite de ce chapitre peuvent se voir comme des \emph{filtres},
c'est-à-dire que l'on peut considérer qu'ils filtrent le flux de
sortie des programmes auxquels ils s'appliquent.  En d'autres termes,
un filtre peut être utilisé avec un tube puisqu'il attend
systématiquement des données sur son flux d'entrée et envoie le
résultat du « filtrage » sur son flux de sortie.
\index{redirections!tubes|)}
\index{tubes|)}
\index{redirections|)}



\section{Les outils de base}
\label{sec-basiques}
Nous donnons ici quelques outils fréquemment utilisés sous \unix{}
dont il faut connaître l'existence. Chacun de ces outils est présenté
avec quelques options, également les plus communes. Pour une
utilisation avancée il faudra se référer aux
\voir[Ch.]{chap-aide}{pages de manuel}. Nous utiliserons pour les
exemples le fichier suivant :
\begin{shellcom}
  ¢usercom«cat fichier.dat»
  Jimi Hendrix 1970
  Jim Morrison 1971
  Janis Joplin 1969
\end{shellcom}


\subsection{Afficher}
\index{afficher}
\index{concaténer}
\label{sec-toolbox-afficher}

Le rôle de la commande \com{cat} est de concaténer
(\anglais{concatenate}) plusieurs fichiers. Le résultat de cette
concaténation étant envoyé sur le flux de sortie. Ainsi :
\begin{flushleft}
  \quad\com{cat} \comarg[1]{fichier} \comarg[2]{fichier} ...
  \comarg[n]{fichier}
\end{flushleft}
concatène les fichiers de 1 à $n$ et les affiche sur le flux de sortie.
Le filtre \com{more} peut être utilisé lorsque l'affichage est trop
long pour le terminal. Les fichiers sont alors affichés page par page :
\begin{flushleft}
   \quad\com{cat} \comarg[1]{fichier} \comarg[2]{fichier} \verb+|+ \com{more}
\end{flushleft}
Selon les systèmes on peut utiliser la commande
\com{less}\footnote{Encore un jeu de mots d'informaticien : le
  remplaçant de l'ancêtre \com{more} est \com{less}, ah-a ah-a, assez
  ri... J'apprends à l'instant que la nouvelle évolution de \com{more}
  s'appelle \com{most}, arf, arf, arf... hum, un peu de sérieux.}
(plus ergonomique) à la place de \com{more}.

\subsection{Trier}
\label{sec-sort}
\index{trier}

C'est la commande \com{sort} :
\begin{shellcom}
  ¢usercom«sort fichier.dat»
  Janis Joplin 1969
  Jimi Hendrix 1970
  Jim Morrison 1971
\end{shellcom}
Par défaut les lignes sont classées selon la première colonne. On peut
classer selon les noms :
\begin{shellcom}
  ¢usercom«sort -k2 fichier.dat»
  Jimi Hendrix 1970
  Janis Joplin 1969
  Jim Morrison 1971
\end{shellcom}
L'option \texttt{-k} pour \emph{key} en anglais spécifie la clef du
tri.  Pour trier selon les dates :
\begin{shellcom}
  ¢usercom«sort -n -k3 fichier.dat»
  Janis Joplin 1969
  Jimi Hendrix 1970
  Jim Morrison 1971
\end{shellcom}
Notez l'utilisation de l'option \texttt{-n} qui permet d'utiliser
l'ordre numérique (par défaut c'est l'ordre
lexicographique). L'option~\texttt{-u} peut quant à elle être utilisée
pour supprimer les doublons.


\subsection{Découper en colonnes}
\index{découper}

La commande \com{cut} permet d'afficher une ou plusieurs colonnes d'un
flux de données :
\begin{shellcom}
  ¢usercom«cut -d' ' -f1,3 fichier.dat»
  Jimi 1970
  Jim 1971
  Janis 1969
\end{shellcom}
Le séparateur de colonnes est par défaut la tabulation. On peut en
spécifier un autre avec l'option \texttt{-d} (pour délimiteur). Ici on
a utilisé le caractère espace comme délimiteur. On a ensuite spécifié
les champs (option \texttt{-f} pour \anglais{field}) numéro~1 et
numéro~3 (respectivement prénom et date). 

Voici pour s'amuser un exemple d'utilisation des tubes :
\begin{shellcom}
  ¢usercom«cut -d' ' -f1,3 fichier.dat | sort -k2 -n»
  Janis 1969
  Jimi 1970
  Jim 1971
\end{shellcom}
Ce qui permet de trier par année le résultat du \com{cut} précédent...

\subsection{Recoller les colonnes}
\label{sec-paste}
\index{recoller}

Supposons que l'on dispose d'un fichier :
\begin{shellcom}
  ¢usercom«cat groups.dat»
  The Experience
  The Doors
  Kosmik Band
\end{shellcom}
On peut alors fusionner ce fichier avec notre fichier d'étoiles
filantes :
\begin{shellcom}
  ¢usercom«paste -d' ' fichier.dat groups.dat»
  Jimi Hendrix 1970 The Experience
  Jim Morrison 1971 The Doors
  Janis Joplin 1969 Kosmik Band
\end{shellcom}
On utilise ici aussi le caractère espace à la place de celui par
défaut (tabulation). On pourra également se référer à la commande
\com{join} qui elle, fusionne deux fichiers selon un champ commun.


\subsection{Compter}
\index{compter}

La commande \com{wc} (pour \anglais{word count}) permet de compter
les caractères, les mots et les lignes d'un flux de données :
\begin{shellcom}
  ¢usercom«wc fichier.dat»
      3       9      54
\end{shellcom}
Ce qui signifie que le fichier en question possède 3 lignes, 9 mots et
54 octets (les espaces et sauts de ligne sont comptabilisés).  On
peut également afficher uniquement l'un de ces nombres, par exemple le
nombre de lignes :
\begin{shellcom}
  ¢usercom«wc -l fichier.dat»
     3
\end{shellcom}
les options pour le nombre de mots et de caractères sont
respectivement \texttt{w} (\emph{word}) et \texttt{c} (\emph{byte}).
\begin{attention}
  Attention, même si aujourd'hui un caractère est encore codé sur un
  octet, on devrait trouver à l'avenir de plus en plus de fichiers
  texte dont les caractères sont codés sur plusieurs octets. Dans ce
  cas on pourra utiliser l'option \texttt{-m} (pour \anglais{multibyte})
  pour compter le nombre de caractères et non d'octets.
\end{attention}


\subsection{Tête-à-queue}
\label{sec-tail-head}
\index{début et fin}

Les commandes \com{head} et \com{tail} comme leur nom l'indique,
permettent d'afficher la tête ou la queue d'un fichier ou du flux
d'entrée. Par exemple :
\begin{shellcom}
  ¢usercom«head -n 2 fichier.dat»
  Jimi Hendrix 1970
  Jim Morrison 1971
\end{shellcom}
affiche les deux premières lignes de notre fichier.  L'option
\texttt{-c} permet d'afficher les $n$ premiers octets :
\begin{shellcom}[noendprompt]
  ¢usercom«head -c 7 fichier.dat»
  Jimi He¢shellprompt
\end{shellcom}
De manière analogue \com{tail} affiche la fin d'un flux :
\begin{shellcom}
  ¢usercom«tail -n 1 fichier.dat»
  Janis Joplin 1969
\end{shellcom}
L'option \texttt{-c} est également disponible. La nuance avec la
commande \com{tail} est qu'en utilisant un nombre de lignes $n$ (ou
d'octets) commençant par \texttt{+}, \com{tail} affiche les données à
partir de la $n$\ieme{} ligne (ou $n$\ieme{} caractère). Ainsi :
\begin{shellcom}
  ¢usercom«tail -n +2 fichier.dat»
  Jim Morrison 1971
  Janis Joplin 1969
\end{shellcom}
affiche le fichier \texttt{fichier.dat} à partir de la deuxième
ligne. Une autre utilisation de la commande \com{tail} est la
surveillance d'un fichier qui grossit. En considérant qu'un fichier
\texttt{data.log} reçoit régulièrement des données, on peut surveiller
les dernières données en utilisant l'option \texttt{-f} de \com{tail}
:
\begin{shellcom}[noendprompt]
  ¢usercom«tail -f data.log»
  189
  123
  234
\end{shellcom}
On interrompt l'affichage avec \toucheCtrl{}\touche{c}\hspace*{-.2em}.


\subsection{Utilitaires disques et fichiers}
\index{utilitaires disques et fichiers|(}

\subsubsection{Chercher}
\index{chercher}

Pour chercher un fichier ou un répertoire on peut utiliser la commande
\com{find}. Cette commande est très puissante et permet de chercher
des fichiers avec de très nombreux critères (type de fichier, nom,
taille, permission, propriétaires, date de modifications, etc.).
Chacun de ces critères peut être combiné par des « ou » ou des « et ».
Nous ne vous proposerons ici que quelques exemples :
\begin{shellcom}
  ¢usercom«find ~ -name core»
  /home/equipe/lozano/LaTeX/test/core
  /home/equipe/lozano/LaTeX/these/core
  /home/equipe/lozano/LaTeX/these/src/core
  /home/equipe/lozano/src/pas/core
  /home/equipe/lozano/install/ummstdod/core
\end{shellcom}
Cette commande cherche les fichiers dont le nom est \texttt{core} à
partir du répertoire privé.
\begin{shellcom}
  ¢usercom«find . -name "*.tex"»
  ./guide-unix.tex
\end{shellcom}
Celle-ci trouve les fichiers dont l'extension est \texttt{.tex} à
partir du répertoire courant. Et :
\begin{shellcom}
  ¢usercom«find ~/cours -type d -a  -name "po*"»
  /home/equipe/lozano/cours/pov
  /home/equipe/lozano/cours/pov.bak
  /home/equipe/lozano/cours/images/pov-hof
\end{shellcom}
trouve tous les répertoires dont le nom commence par \texttt{po}.
L'option \texttt{-a} couple les critères par un « et ». Cette option
est implicite et peut donc être omise.
\begin{note}
  La commande \com{find} peut-être particulièrement coûteuse lorsqu'on
  doit scanner toute l'arborescence. C'est pour cette raison que
  certains \unix{} proposent la commande \com{locate} qui utilise un
  fichier de données contenant l'emplacement de tous les fichiers sur
  le système. Ce fichier de données doit être mis à jour régulièrement
  (généralement une ou deux fois par jour) par \com{cron}. L'avantage
  de cette méthode est bien entendu la rapidité de la recherche,
  l'inconvénient réside dans le fait que le fichier de données n'est
  pas à jour en temps réel et ne contient pas d'autres informations
  que les noms des fichiers (et pas leur contenu).
\end{note}

\subsubsection{Obtenir des informations}
\index{fichier!informations}
\index{disques!informations}

\index{fichier!taille}
\index{répertoire!taille}
La commande \com{du} donne la taille qu'occupe un répertoire ou un
fichier.
\begin{shellcom}
  ¢usercom«du -b guide-unix.tex»
  71680   guide-unix.tex
\end{shellcom}
L'option \texttt{-b} affiche en octets (\anglais{byte}).  Si l'argument
de cette commande est un répertoire le calcul d'occupation disque est
fait récursivement sur tous les fichiers et sous-répertoires :
\begin{shellcom}
  ¢usercom«du -k ~/cours»
  590     /home/equipe/lozano/cours/unix
  380     /home/equipe/lozano/cours/c++
  1755    /home/equipe/lozano/cours/images
  7736    /home/equipe/lozano/cours/pov
\end{shellcom}
L'option \texttt{-k} est utilisée ici pour afficher en kilo-octets. On
peut obtenir le total de chaque sous-répertoire grâce à l'option
\texttt{-s} (\anglais{summarize}) :
\begin{shellcom}
  ¢usercom«du -sh ~/cours»
  10M    /home/equipe/lozano/cours
\end{shellcom}

\index{partition!taux d'occupation}
La commande \com{df} donne le taux d'encombrement des partitions
relatives aux \voir{sec-unix-arborescence}{disques locaux} et
réseaux.  L'option \texttt{-h} (\anglais{human readable}) de cette
commande donne l'occupation des partitions en méga-octets,
giga-octets, etc.
\begin{shellcom}
  ¢usercom«df -h»
  Filesystem            Size  Used Avail Use% Mounted on
  /dev/sda5             197M   56M  131M  30% /tmp
  /dev/sda3             972M  471M  450M  51% /usr
  /dev/sda6             197M  9.8M  177M   5% /var
  count:/export/home0   2.8G  1.7G 1015M  63% /mnt/count/home0
  count:/export/home5   969M  247M  672M  27% /mnt/count/home5
  count:/export/softs   969M  238M  681M  26% /mnt/count/softs
  count:/export/home6   969M  614M  305M  67% /mnt/count/home6
  mailhost:/var/spool/mail
                        972M   53M  869M   6% /var/spool/mail
\end{shellcom}


\subsubsection{Archivage}
\index{archivage}
\index{fichier!archivage}
\label{sec-unix-targzip}

\begin{gnuposix}
  Avertissement : cette section fera sans doute faire des bonds aux
  puristes qui diront à juste titre que les seules commandes
  standardisées par Posix sont \com{pax} et \com{compress}. J'ai
  choisi ici de présenter \com{tar} et \com{gzip} (et même
  \com{bzip2}) car il s'agit de commandes très répandues et également
  parce que j'ai l'habitude de les utiliser !
\end{gnuposix}

L'archivage est une tâche très fréquente lorsqu'on manipule des
fichiers. \unix{} distingue deux opérations dans l'archivage :
\begin{enumerate}
\item l'archivage proprement dit qui consiste à rassembler plusieurs
  fichiers dans un seul, qui constitue l'\emph{archive} ;
\item la compression de l'archive pour en optimiser la taille. Cette
  opération doit être effectuée sauf dans de rares cas où les fichiers
  archivés sont déjà compressés (tas de fichiers Jpeg ou Pdf par
  exemple).  \index{fichier!compression} \index{compression de
    fichiers}
\end{enumerate}
La première opération est réalisée par la commande \com{tar}
(\anglais{tape archiver}, utilisée historiquement pour archiver sur
bande magnétique), la deuxième par \com{gzip}\footnote{\com{gzip} est
  devenu le standard \emph{de facto} de compression sous \unix{} ;
  malgré tout, on ne trouve sous certains systèmes que la commande
  \com{compress} qui suit peu ou prou la même syntaxe que \com{gzip}.
  Notons également que \com{gzip} sera à moyen terme probablement
  supplanté par \com{bzip2} qui permet d'obtenir un meilleur taux de
  compression.}.  Nous donnerons ici un exemple très classique
d'archivage : l'archivage des fichiers contenus dans un répertoire :
\begin{shellcom}
  ¢usercom«pwd»
  /home/equipe/lozano/cours/unix
  ¢usercom«cd ..»
  ¢usercom«tar cvf ~/transfert/unix.tar unix ¢comcomment«création de l'archive»»
  unix
  unix/arborescence.eps
  unix/arborescence.fig
  unix/flux.fig
  «¢normalfont... etc ...» 
  «¢mbox«»»
  ¢usercom«ls -l ~/transfert/unix.tar»
  -rw-r----- 1 lozano equipe 768000 Dec 13 14:49 unix.tar
  ¢usercom«gzip ~/transfert/unix.tar ¢comcomment«compression de l'archive»»
  ¢usercom«ls -l ~/transfert/unix.tar.gz»
  -rw-r----- 1 lozano equipe 239039 Dec 13 14:49 unix.tar.gz
\end{shellcom}
Voici quelques explications :
\begin{itemize}
\item l'option \texttt{c} de \com{tar} \emph{crée} l'archive (\anglais{create}) ;
\item l'option \texttt{f} précise que l'archive à créer est un fichier
  (\anglais{file}) (et non un enregistrement sur une bande
  magnétique) ;
\item l'option \texttt{v} (\anglais{verbose}) permet d'afficher les
  fichiers (et leur chemin) en cours d'archivage ;
\item le nom de l'archive suit (\texttt{unix.tar}) puis le nom du
  répertoire contenant les fichiers à archiver ;
\item la commande \com{gzip} compresse l'archive en la
  \emph{renommant} avec le suffixe~\texttt{.gz}.
\end{itemize}

L'opération  de « désarchivage » se déroule en utilisant les
commandes \com{gzip} et \com{tar} dans l'ordre inverse. Par exemple,
pour décompacter l'archive précédente dans le répertoire
\verb+~/tmp+ :
\begin{shellcom}
  ¢usercom«cd ~/tmp»
  ¢usercom«gzip -d ~/transfert/unix.tar.gz ¢comcomment«décompression»»
  ¢usercom«tar xvf ~/transfert/unix.tar ¢comcomment«décompactage»»
  unix/
  unix/arborescence.eps
  unix/arborescence.fig
  unix/flux.fig
  «¢normalfont... etc ...» 
  «¢mbox«»»
  ¢usercom«ls unix»
  Makefile    disques.fig   groups.dat      guide-unix.tex~
  Makefile~   ecoute.sh     guide-unix.aux  guide-unix.toc  
  patati.eps  fichier.dat   guide-unix.dvi  missfont.log    
  «¢normalfont... etc ...» 
\end{shellcom}
Notez l'option \texttt{-d} de \com{gzip} pour décompresser, et l'option
\texttt{x} de \com{tar} pour extraire (\anglais{extract}).

La version \gnu{} de \com{tar} permet d'appeler \com{gzip} automatiquement
grâce à l'option \texttt{z}, ce qui permet de réaliser l'archivage et
la compression sur la même ligne de commande :
\begin{shellcom}
  ¢usercom«tar xfz ~/transfert/unix.tar.gz»
\end{shellcom}
On pourra également appeler \com{bzip2} pour les archives compressées
avec cet utilitaire, grâce à l'option \texttt{j} :
\begin{shellcom}
  ¢usercom«tar xfj ~/transfert/unix.tar.bz2» 
\end{shellcom}
Puisque nous avons vu les tubes, les puristes rétorqueront que même
sans la version \gnu{} de \com{tar} il est possible de n'utiliser qu'une
seule ligne de commande ; ceci en tirant partie du fait que \com{gzip}
et \com{tar} suivent le modèle des « trois flux » :
\begin{shellcom}
  ¢usercom«tar cf - unix | gzip -c > ~/transfert/unix.tar.gz»
\end{shellcom}
Petites explications :
\begin{itemize}
\item le caractère \texttt{-} indique à \com{tar} que l'archive doit être envoyé sur le flux de sortie ;
\item \com{gzip} compresse les données provenant du flux d'entrée et
  les envoie sur le flux de sortie grâce à l'option \texttt{-c} ;
\item le résultat est redirigé dans le fichier \verb+~/transfert/unix.tar.gz+.
\end{itemize}
La décompression sur une ligne de commande peut être faite comme suit :
\begin{shellcom}[noendprompt]
  ¢usercom«gzip -dc ~/transfert/unix.tar.gz | tar xvf -»
\end{shellcom}
\index{utilitaires disques et fichiers|)}

\section{Le shell en tant que langage}
\label{sec-shell-langage}
\index{shell!langage}

Jusqu'à maintenant nous avons vu le shell comme un interpréteur de
commandes qui se « contente » d'exécuter les commandes qu'on lui passe.
En réalité le shell est un langage de programmation assez évolué
permettant d'automatiser un grand nombre de tâches quotidiennes. Le
shell (et nous parlerons ici essentiellement du shell \texttt{bash})
dispose donc de variables, de tableaux, de structures de contrôle (if,
while, for), et également de la notion de fonctions. Toutes ces
fonctionnalités sont détaillées au chapitre~\ref{chap-developper}.

Nous vous présenterons ici par le biais d'une « étude de cas » deux
fonctionnalités intéressantes du shell : la \emph{substitution de
  commande} et la boucle « pour ». Le problème est le suivant : suite
à une erreur lors d'un transfert de fichier, on a dans un répertoire
une liste de fichiers dont les noms sont en majuscules. On veut
transformer ces noms en minuscules. L'algorithme que nous allons
implémenter en langage de commande pourrait s'écrire :
\begin{algo}[noendkeyword]
  \FORGEN{chaque fichier $f$ dont l'extension est \texttt{JPG}}
  \STATE{renommer $f$ en minuscule}
  \ENDFOR
\end{algo}

\subsection{Afficher des informations avec \com{printf}}

La commande \com{printf} est un « grand classique » qu'on retrouve
dans plusieurs langages de programmation (C, Php pour ne citer que
ceux-là). L'idée de cette commande est de réaliser un affichage
\emph{formaté} (c'est le « \verb+f+ » de \com{printf}). Cette commande
remplace souvent avantageusement la commande \com{echo} bien plus
limitée.

Pour ce qui est de son utilisation, \com{printf} attend une
\emph{chaîne de format} précisant l'allure de l'affichage, suivie d'un
nombre variable d'arguments. Par exemple :
\begin{shellcom}
  ¢usercom«printf "%d est un nombre pseudo-aléatoire\n" $RANDOM»
  14234 est un nombre pseudo-aléatoire
\end{shellcom}
Ce premier exemple peut être traduit en français par : je souhaite
afficher un entier (\verb+%d+) en base 10, suivi de la phrase « est un
nombre pseudo-aléatoire », suivie d'un saut de ligne (\verb+\n+). Dans
l'exemple suivant la chaîne de format est suivie par 2 arguments :
\begin{shellcom}
  ¢usercom«printf "En voici 2 autres : %d et %d\n" $RANDOM $RANDOM»
  En voici 2 autres : 23943 et 83442
\end{shellcom}
On comprend donc qu'à chaque \verb+%d+ contenu dans la chaîne de format
doit correspondre un argument. \com{printf} reconnaît un certain
nombre de type de données, entre autres :
\begin{itemize}
\item \verb+%d+ pour afficher un entier en base décimale (on peut
  également utiliser \verb+%o+ pour la base octale et \verb+%x+ pour la
  base héxadécimale) ;
\item \verb+%f+ pour afficher un nombre à virgule flottante ;
\item \verb+%s+ pour afficher une chaîne ;
\end{itemize}
Par ailleurs, chacun de ces types de données accepte un certain nombre
de modificateurs. En voici quelques-uns :
\begin{shellcom}
  ¢usercom«printf "==[%010d]==\n" $RANDOM»
  ==[0000007052]==
\end{shellcom}
pour afficher un entier sur 10 caractères comblés avec des zéros si
nécessaire.\begin{shellcom}
  ¢usercom«printf "==[%-20s]==\n" $USER»
  ==[lozano              ]==
\end{shellcom}
pour une chaîne produite sur 20 caractères alignée à gauche
(signe~«\verb+-+»). D'autres exemples devraient vous venir à l'esprit
après avoir lu le « fantastique manuel ».

\subsection{Substitution de commande}
\label{sec-toolbox-substitution-commande}

\index{commande!substitution}
\index{substitution de commande}

La substitution de commande est un mécanisme permettant d'exploiter la
sortie d'une commande dans l'appel d'une autre commande. La syntaxe
permettant de réaliser la substitution est \verb+$(...)+. Pour
comprendre comment cela se passe, soit la commande suivante, affichant
le nombre de ligne du fichier \texttt{fichier.dat} :
\begin{shellcom}
  ¢usercom«wc -l fichier.dat»
  3 fichier.dat
\end{shellcom}
On se débarrasse pour l'exemple du nom de fichier :
\begin{shellcom}
  ¢usercom«wc -l fichier.dat | cut -f1 -d' '»
  3
\end{shellcom}
On peut également s'en débarasser en utilisant une redirection :
\begin{shellcom}
  ¢usercom«wc -l < fichier.dat»
  3
\end{shellcom}
Si on voulait maintenant utiliser cette valeur dans une commande :
\begin{shellcom}
  ¢usercom«echo Il y a $(wc -l < fichier.dat) musiciens»
  Il y a 3 musiciens
\end{shellcom}
%$
Ici, dans un premier lieu, ce qui se trouve entre les
caractères~\verb+$(...)+ est remplacé par ce qu'affiche la commande
\texttt{wc -l ...}, c'est-à-dire~3, puis la commande \com{echo}
réalise l'affichage. Avec \com{printf} on écrira :
\framaallongepage{1}
\begin{shellcom}
  ¢usercom«printf 'Il y a %d musiciens' "$(wc -l < fichier.dat)"»
\end{shellcom}
Un autre exemple : sachant que la commande
\com{date} affiche la date sous un format que l'on peut définir :
\begin{shellcom}
  ¢usercom«date +%h%d»
  Dec15
\end{shellcom}
Il est possible de sauvegarder un fichier comme suit :
\begin{shellcom}
  ¢usercom«cp source.cc source.cc.$(date +%h%d)»
\end{shellcom}
%$
La commande \com{cp} ci-dessus copie le fichier \texttt{source.cc} en
un fichier dont le nom est \texttt{source.cc.} concaténé avec ce que
renvoie la commande \com{date}.
\begin{shellcom}
  ¢usercom«ls source.cc*»
  source.cc     source.cc.Dec15
\end{shellcom}
\begin{note}
  Il existe une autre syntaxe pour la substitution de commande. En
  lieu et place de la forme \verb+$(+\comarg{commande}\verb+)+, certains
  vieux shells ne disposent que de la syntaxe utilisant le
  \anglais{backquote} (\verb+`+) :
  \begin{flushleft}
    \verb+`+\comarg{commande}\verb+`+
  \end{flushleft}
  \continuenota%
  Qu'on se le dise...
\end{note}

\subsection{La structure \com{for} de \com{bash}}
\label{sec-structure-for}
\index{boucle \texttt{for}}
\index{shell!boucle \texttt{for}}

Le shell \texttt{bash} propose une structure de contrôle nommée
\texttt{for} que l'on peut traduire par « pour chaque », la syntaxe en
est la suivante :
\begin{shellcom}[noendprompt]
  for ¢comarg«variable» in ¢comarg«liste» ; do  ¢comarg[1]«com» ; ¢comarg[2]«com» ; ... ;  done
\end{shellcom}
On peut donc écrire quelque chose de la forme :
\begin{shellcom}
  ¢usercom«for F in *.fig ; do echo "$F.tmp" ; done»
  arborescence.fig.tmp
  disques.fig.tmp
  flux.fig.tmp
  pipes.fig.tmp
\end{shellcom}
%$
On affiche ici, pour \texttt{F} prenant les valeurs de l'expansion de
\texttt{*.fig}, la variable \texttt{F} suivie de la chaîne
\texttt{.tmp}, ce qui a, je vous l'accorde, peu d'intérêt.
\begin{attention}
  Notez ici les guillemets dans la syntaxe \verb+echo "$F.tmp"+ qui a
  pour but de ne pas considérer \verb+$F.tmp+ comme une liste si
  jamais il existe un fichier dont le nom contient un espace.
\end{attention}
On peut imaginer un autre exemple, qui utilise l'expansion de commande
:
\begin{shellcom}
  ¢usercom«for F in $(find . -name '*.jpg') ; do xzgv "$F" & ; done»
\end{shellcom}
qui lance \com{xzgv} (un visualiseur d'image) en tâche de fond, pour
chaque fichier portant l'extension \texttt{jpg} dans le répertoire
courant et ses sous-répertoires.
\begin{attention}
  L'exemple ci-dessus possède une limitation : il ne fonctionne pas si
  le nom des fichiers portant l'extension \texttt{jpg} contient un
  espace (ou un saut de ligne, ce qui est plutôt rare...). Nous vous
  renvoyons au paragraphe sur la variable
  \voir{sec-developper-IFS}{\texttt{IFS}} pour pallier ce problème.
\end{attention}


\subsection{Revenons à nos moutons}
« Nos moutons », c'est notre problème de transformation de noms de
fichiers majuscules en minuscules. La commande \com{tr}
(\anglais{translate}) a la capacité de transformer une chaîne de
caractères :
\begin{shellcom}
  ¢usercom«echo SALUT | tr A-Z a-z»
  salut
\end{shellcom}
Donc pour renommer nos fichiers on peut imaginer la commande suivante,
qui rassemble les concepts vus dans ce paragraphe :
\begin{shellcom}
  ¢usercom«for F in $(find . -name '*JPG') ; do » ¢touche«Entrée»
  ¢usercomwmp«mv "$F" "$(echo $F | tr A-Z a-z)"; done»
\end{shellcom}
Ce qui peut se traduire en français par : tous les fichiers finissant
par \texttt{JPG} doivent être renommés en utilisant le résultat de
l'affichage du nom original filtré par \com{tr} (c'est-à-dire en
minuscule).
\begin{note}
  Cette commande présente l'inconvénient de ne pas fonctionner si les
  images \texttt{JPG} en question se trouvent dans des
  sous-répertoires dont les noms contien\-nent des majuscules. Elle
  possède en outre les mêmes limitations que l'exemple précédent (noms
  de fichier contenant un espace ou un saut de ligne). Par ailleurs,
  il est bon de savoir que ce genre de tâches (copie/déplacement de
  fichiers en salve) peut être réalisé de manière sûre (en
  limitant les risques d'écrasement de fichiers) grâce à l'utilitaire
  \texttt{mmv} disponible dans toutes les bonnes crémeries.
\end{note}
\begin{note}
  \index{prompt} Dans la commande précédente, l'utilisateur a appuyé
  sur la touche \touche{Entrée} avant que la commande \com{find} ne
  soit complète.  Le shell affiche alors un autre prompt qui se
  distingue du prompt habituel, pour informer l'utilisateur que la
  commande est incomplète. La manière d'afficher ce prompt est définie
  par la \voir{sec-conf-shell-prompt}{variable \envvar{PS2}}. Dans la
  suite de ce document nous n'indiquerons plus la pression sur la
  touche \touche{Entrée} dans les exemples.
\end{note}


\section{\com{grep} et la notion d'expressions régulières}
\label{sec-regexp}
\index{expression régulière}
\index{regular expression|see{expression régulière}}

La commande \com{grep} est une commande « célèbre » d'\unix{} et
permet d'afficher les lignes d'un flux correspondant à un \emph{motif}
donné. Par exemple :
\begin{shellcom}
  ¢usercom«grep Hendrix fichier.dat»
  Jimi Hendrix 1970
\end{shellcom}
ou : 
\begin{shellcom}
  ¢usercom«grep Jim fichier.dat»
  Jimi Hendrix 1970
  Jim Morrison 1971
\end{shellcom}
Les motifs en question (\texttt{Hendrix} et \texttt{Jim}) dans les
deux exemples précédents se nom\-ment en jargon \unix{}\footnote{Et plus
  généralement dans le domaine de l'étude des langages informatiques.}
une \emph{expression régulière} (ou une \emph{expression
  rationnelle}\footnote{J'utilise pour ma part la première forme, ne
  pas taper svp...})  (\anglais{regular expression} parfois abrégé en
\emph{regexp}).  L'étude de la théorie des expressions régulières
pourrait faire l'objet d'un document de la taille de ce guide de
survie. Voici cependant sous forme d'un tableau à quoi correspondent
quelques-uns des motifs d'une expression régulière.
\begin{center}
\begin{tabular}{l|l|l}
\verb+.+ & désigne n'importe quel caractère & B\\
\verb+*+ & zéro ou plusieurs fois l'élément précédent&B\\
\verb+?+ & zéro ou une fois l'élément précédent&E\\
\verb+++ & une ou plusieurs fois l'élément précédent&E\\
\verb+^+ & correspond au début de la ligne&B\\
\verb+$+ & correspond à la fin de la ligne&B\\%$
\verb+[abc]+ & un caractère parmi \verb+abc+&B  \\
\verb+[^abc]+ & tout caractère sauf \texttt{a}, \texttt{b} ou \texttt{c}&B\\
\verb+{+\comarg{n}\verb+}+ & exactement \comarg{n} fois l'élément précédent&E\\
\verb+{+\comarg{n}\verb+,+\comarg{m}\verb+}+ & au moins \comarg{n} fois et au plus \comarg{m} fois l'élément précédent\footnotemark.&E
\end{tabular}
\end{center}
\footnotetext{Dans cette expression, \comarg{n} ou \comarg{m} peuvent
  être omis.} 
\begin{note}
  La version de \com{grep} utilisée pour l'exemple est celle de chez
  \gnu{}. Cette commande comprend trois ensembles d'expressions
  régulières (basique, étendue et Perl). Dans le tableau précédent les
  deux premiers ensembles sont indiqués respectivement par B et E dans
  la colonne de droite. 
\end{note}
Voyons quelles sont les applications de ces motifs. La commande :
\begin{shellcom}
  ¢usercom«grep '197.$' fichier.dat»
  Jimi Hendrix 1970
  Jim Morrison 1971
\end{shellcom}
%$
récupère les lignes finissant par la chaîne « 197 » suivie d'un caractère
quel qu'il soit.
\begin{attention}
  Notez qu'il est sage de « quoter » (entourer par des apostrophes)
  le motif car beaucoup de caractères composant les expressions
  régulières sont des caractères spéciaux pour le shell.
\end{attention}
La commande :
\begin{shellcom}
  ¢usercom«grep '^Ji' fichier.dat»
  Jimi Hendrix 1970
  Jim Morrison 1971  
\end{shellcom}
récupère les lignes commençant par la chaîne « Ji ». La commande :
\begin{shellcom}
  ¢usercom«grep '^J.*1970' fichier.dat»
  Jimi Hendrix 1970
\end{shellcom}
récupère les lignes commençant par « J » et contenant « 1970 ». La
commande :
\begin{shellcom}
  ¢usercom«grep -E 'Jimi?' fichier.dat»
  Jimi Hendrix 1970
  Jim Morrison 1971  
\end{shellcom}
récupère les lignes contenant « Jimi » ou « Jim ». Il est ici nécessaire
d'utiliser l'option \texttt{-E} (\anglais{extended regexp}) pour
pouvoir utiliser le caractère \texttt{?}.  Parmi les options
intéressantes de \com{grep}, en voici trois :
\begin{itemize}
\item l'option \texttt{-w} (\anglais{word}) permet de restreindre le
  résultat de la correspondance à un mot entier :
  \begin{shellcom}
    ¢usercom«grep Jim fichier.dat»
    Jimi Hendrix 1970
    Jim Morrison 1971  
  \end{shellcom} mais :
  \begin{shellcom}
    ¢usercom«grep -w Jim fichier.dat»
    Jim Morrison 1971
  \end{shellcom} 
  \begin{gnuposix}
    Pour le Gnu \com{grep} qui dispose de l'option \texttt{-w}, un «
    mot » est défini comme un ensemble de caractères parmi les
    lettres, les chiffres et le caractère souligné~«\verb+_+»
    (\anglais{underscore}).
  \end{gnuposix}
\item l'option \texttt{-i} (\anglais{ignore case}) permet d'ignorer la
  casse (minuscule ou majuscule) dans la correspondance avec le motif :
  \begin{shellcom}
    ¢usercom«grep -i jimi fichier.dat»
    Jimi Hendrix 1970
  \end{shellcom}
\item l'option \texttt{-v} (\anglais{revert match}) inverse le sens de
  la correspondance et n'affiche que les lignes qui ne correspondent
  pas à l'expression régulière :
  \begin{shellcom}
    ¢usercom«grep -vi jim fichier.dat»
    Janis Joplin 1969
  \end{shellcom}
\end{itemize}
La commande \com{grep} a été utilisée pour les exemples avec un
fichier comme argument, mais elle peut bien évidemment faire la
correspondance sur son flux d'entrée et être composée avec des tubes.
Par exemple, la commande :
\begin{shellcom}
  ¢usercom«ps -ef | grep '^lozano'»
  lozano   11396     1  0 14:53 ?        00:00:00 xterm -ls
  lozano   11398 11396  0 14:53 pts/2    00:00:00 -bash
  lozano   11512 11398  0 14:58 pts/2    00:00:00 ps -ef
  lozano   11513 11398  0 14:58 pts/2    00:00:00 grep lozano
  ...
\end{shellcom}
filtre le résultat de la commande \com{ps} en ne renvoyant que les
lignes commençant par la chaîne \texttt{lozano}. Ce qui permet d'avoir
la liste des processus dont \texttt{lozano} est le propriétaire. Notez
enfin que la commande \verb+ps -fu lozano+ aurait donné un résultat
identique...


\section{\com{awk}}
\label{sec-awk}

On peut voir \com{awk}\footnote{Du nom de ses auteurs initiaux en
  1977, Alfred V.  \textsc{Aho}, Peter J. \textsc{Weinberger}, and
  Brian W.  \textsc{Kernighan}} comme un filtre permettant d'effectuer
des modifications sur les lignes d'un flux ; ces lignes étant
sélectionnées par l'intermédiaire d'une expression régulière (dans le
même esprit que \com{grep}) ou d'une condition quelconque. Cependant
\com{awk} dispose d'un langage de programmation spécial (dont la
syntaxe pourrait se rapprocher de celle du C) permettant de manipuler
et modifier les lignes sélectionnées comme un ensemble de champs (à
l'instar de \com{cut}). La syntaxe générale d'un programme \com{awk}
est une suite de \emph{règles} définies comme un couple de
\emph{motifs} et d'\emph{actions} :
\begin{itemize}
\item les motifs permettent de sélectionner des lignes dans le flux et
  sont  :
  \begin{itemize}
    \index{expression régulière}
  \item soit des expressions régulières et doivent être délimités par des
    \verb+/+ (par exemple  \verb+/abc/+) ;
  \item soit des expressions simples (égalité, inégalité,...) ;
  \end{itemize}
\item les actions sont exécutées pour chaque motif et sont délimitées
  par des \verb+{+ et \verb+}+ (comme par exemple dans l'expression
  \verb+{print "bonjour\n"}+).
\end{itemize}
Ces caractères  ayant une signification particulière pour le shell, il %$
faut les \voir{sec-unix-expansion}{protéger} par des cotes \verb+'+ .

Voici un petit programme \com{awk} :
\begin{shellcom}
  ¢usercom«awk '{print $1,$3}' fichier.dat»
  Jimi 1970
  Jim 1971
  Janis 1969
\end{shellcom}
Dans cet exemple, on sélectionne toutes les lignes (pas de motif
spécifié) et on affiche les champs numéro 1 (\verb+$1+) et 3 %$
(\verb+$3+) ---~correspondant aux prénoms et %$
dates~--- de chacune de ces lignes. \com{awk} délimite les champs par un
nombre quelconque d'espaces ou tabulations\footnote{Comportement qui
  peut être changé par l'option \texttt{-F}.}. On peut
utiliser une expression régulière comme ceci :
\begin{shellcom}
  ¢usercom«awk '/^Jim/{print $1,$3}' fichier.dat»
  Jimi 1970
  Jim 1971
\end{shellcom}
où l'expression régulière « \^{}Jim » spécifie « ligne commençant par
\texttt{Jim} ».  Voici un autre exemple :
\begin{shellcom}
  ¢usercom«awk '$3==1970{print "En "$3" "$2"..."}' fichier.dat»
  En 1970 Hendrix...
\end{shellcom}
%$
Ici on a sélectionné les lignes à l'aide d'une expression simple : un
test d'égalité sur le 3\ieme{} champ.  On peut en outre effectuer un
appariement de motif uniquement sur un champ particulier, grâce à
l'opérateur~\verb+~+ de \com{awk} :
\begin{shellcom}
  ¢usercom«awk '$2 ~ /lin$/ {print "Cette chère "$1"..."}' fichier.dat»
  Cette chère Janis...
\end{shellcom}
%$
L'opérateur \verb+~+  permet en effet de filtrer le flux avec une
correspondance sur un champ (ici le 2\ieme{} doit finir par « \texttt{lin} »).

On peut utiliser des variables en leur donnant un nom. Leur
manipulation (affectation, comparaison,...) s'apparente à celle du
langage C. On peut par exemple faire la moyenne des années
comme suit :
\begin{shellcom}
  ¢usercom«awk '{s+=$3} END{print "moyenne : "s/NR}' fichier.dat»
  moyenne : 1970
\end{shellcom}
%$
on a ajouté pour chaque ligne la valeur du 3\ieme{} champ à la variable
\texttt{s}. Et dans la clause \texttt{END} (qui n'est exécutée qu'à la
fin), on divise par \texttt{NR} le nombre d'enregistrements (lignes)
du flux (\anglais{number of record}). La commande \texttt{print} peut
également être remplacée par la commande \texttt{printf}.

\com{awk} dispose d'un langage complet, possédant des structures de
contrôle (boucle for, boucle while, if,...), un ensemble d'outils
pour le traitement des chaînes de caractères, ainsi que des fonctions
mathématiques (racine carré, log, fonctions trigonométriques,
etc.). 


\section{\com{sed}}
\label{sec-sed}

\com{sed} (pour \anglais{stream editor}) est, comme son nom l'indique,
un éditeur de flux. Il permet de faire des
transformations sur des chaînes de caractères.  Nous donnerons un
exemple simple qui consiste à remplacer une chaîne de caractères dans
un fichier. La syntaxe est alors :
\begin{comsyntax}
  sed 's/\comarg{recherche}/\comarg{remplace}/' \comarg{fichier}
\end{comsyntax}
ou 
\begin{comsyntax}
  sed 's/\comarg{recherche}/\comarg{remplace}/' < \comarg{fichier}
\end{comsyntax}
où \comarg{recherche} est la chaîne recherchée et \comarg{remplace} la
chaîne qui servira de remplacement. Par exemple, pour changer le nom
d'une variable dans un source C :
\begin{shellcom}
  ¢usercom«sed 's/iterateur/compteur/g' < fichier.c > nouveau.c»
\end{shellcom}
Ici \com{sed} lit le contenu de \texttt{fichier.c} et réalise le
remplacement (commande \texttt{s} pour \anglais{substitute}) de la
chaîne « \texttt{iterateur} » par la chaîne « \texttt{compteur} ». La
commande \texttt{g} (\anglais{global}) assure que la chaîne sera
remplacée plusieurs fois par ligne le cas échéant. Enfin notons que,
comme \com{awk}, \com{sed} renvoie son résultat sur le flux de sortie.

En imaginant que l'on veuille faire la même opération sur plusieurs
fichiers, on peut utiliser la commande \texttt{for} vue précédemment :
\begin{shellcom}
  ¢usercom«for F in *.c ; do »
  ¢usercomwmp«sed 's/iterateur/compteur/g' < "$F" > tmp && »
  ¢usercomwmp«mv -f tmp "$F"; done»
\end{shellcom}



La commande \texttt{s} de \com{sed} dispose également de « registres
» qui permettent de réutiliser des parties de la chaîne qui
correspondent aux motifs spécifiés.  Considérons l'exemple suivant : à
partir de notre célèbre fichier \texttt{fichier.dat} contenant :
\begin{fichier}[label=fichier.dat]
  Jimi Hendrix 1970
  Jim Morrison 1971
  Janis Joplin 1969
\end{fichier}
on veut construire la liste :
\begin{verbatim}
  Hen1970
  Mor1971
  Jop1969
\end{verbatim}
Soit l'expression régulière suivante :
\begin{verbatim}
  ^[A-Za-z]* [A-Z][a-z]{2}[a-z]* [0-9]{4}
\end{verbatim}
\index{expression régulière} cette expression est capable de
reconnaître :
\begin{itemize}
\item un début de ligne suivi d'une suite de caractères en minuscules
  ou en majuscules (\verb+[A-Za-z]*+) ;
\item suivie d'un espace ;
\item suivi d'une majuscule ((\verb+[A-Z]+), suivie de deux minuscules
  (\verb+[a-z]{2}+), suivies d'un nombre quelconque de lettres en
  minuscules (\verb+[a-z]*+) ;
\item suivi d'un espace ;
\item suivi d'exactement quatre chiffres (\verb+[0-9]{4}+).
\end{itemize}
De manière à utiliser cette expression avec \com{sed}, il est
nécessaire d'utiliser l'option \texttt{-r} forçant l'utilisation des
expressions régulières étendues (\anglais{extended regexp}) :
\begin{shellcom}
  ¢usercom«sed -r 's/^[A-Za-z]* [A-Z][a-z]{2}[a-z]* [0-9]{4}/xxx/' fichier.dat»
  xxx
  xxx
  xxx
\end{shellcom}
Toutes les lignes correspondent à l'expression régulière, elles sont
donc remplacées par « \texttt{xxx} ». De manière à réutiliser des
parties de l'expression régulière dans le remplacement, on les
délimite par les caractères \verb+(+ et \verb+)+, les registres ainsi
formés sont nommés \verb+\1+, \verb+\2+, etc.  Dans notre exemple nous
allons récupérer les trois premières lettres du nom et l'année :
\begin{shellcom}
  ¢usercom«sed -r 's/^[A-Za-z]* ([A-Z][a-z]{2})[a-z]* ([0-9]{4})/\1\2/' \
           fichier.dat»
  Hen1970
  Mor1971
  Jop1969
\end{shellcom}
Notez la position des couples de caractères \verb+(+ et \verb+)+. Le
registre \verb+\1+ contient le nom, et le registre \verb+\2+ contient
l'année. 
\begin{gnuposix}
  Sans la version \gnu{} de \com{sed} qui propose l'option
  \texttt{-r}, on doit utiliser les caractères \verb+\(+ et \verb+\)+
  pour délimiter les motifs à réutiliser. D'autre part on utilise
  l'expression régulière pour préciser qu'un motif est répété $n$ fois
  doit être \verb+\{+$n$\verb+\}+ et non \verb+{+$n$\verb+}+. Ce qui
  donne :
\begin{verbatim}
  ^[A-Za-z]* \([A-Z][a-z]\{2\}\)[a-z]* \([0-9]\{4\}\)
\end{verbatim}
  \continuenota pour la partie gauche de la commande \texttt{s}...
\end{gnuposix}

Notons, juste pour rire, que la solution à ce problème en utilisant
\com{awk} est la suivante :
\begin{shellcom}
  ¢usercom«awk '{print substr($2,0,3)$3}' fichier.dat»
  Hen1970
  Mor1971
  Jop1969
\end{shellcom}
Mais gardons à l'esprit que cet exemple vise surtout à illustrer la
puissance des expressions régulières.

\section{Études de cas}
\label{sec-cas}

Nous vous présentons dans cette section trois « études de cas »
mettant en application la plupart des outils standard d'\unix{}.

\subsection{Manipuler la liste des utilisateurs}
\index{utilisateur!liste des}

Sur un système \unix{}, on peut obtenir la liste des utilisateurs
potentiels du système grâce à la commande \verb+getent passwd+%
\footnote{Sur votre système il peut s'agir d'une autre
  commande...}. Cette commande renvoie le fichier contenant les
caractéristiques de chaque utilisateur :
\begin{shellcom}
  ¢usercom«getent passwd »
  merle_g:x:1321:1300:MERLE Gerard/home/4annee/merle_g:/bin/tcsh
  talji_y:x:966:900:TALJI Yacine:/home/1/talji_y:/bin/tcsh
  maurin_r:x:945:900:MAURIN Raphaelle:/home/1/maurin_r:/bin/tcsh
  sergen_c:x:1415:1400:SERGENT Chris:/home/5/sergen_c:/bin/tcsh
  arnaud_l:x:1416:1400:ARNAUD Lionel:/home/5/arnaud_l:/bin/tcsh
  lepeti_c:x:1408:1400:LEPETIT Claire:/home/5/lepeti_c:/bin/tcsh
  «¢normalfont ... etc ...»
\end{shellcom}
Le flux renvoyé a la structure suivante, de gauche à droite :
\begin{itemize}
\item le \emph{login\_name} de l'utilisateur ;
\item un « \texttt{x} » pour le champ mot de passe ;
\item le numéro (\texttt{uid} pour \anglais{user identifier}) de l'utilisateur ;
\item le numéro (\texttt{gid} pour \anglais{group identifier}) du groupe de l'utilisateur ;
\item son nom en clair ;
\item son répertoire racine ;
\item son shell de connexion.
\end{itemize}
On peut noter que ces champs sont séparés par des~«:».  Le nombre
d'utilisateurs potentiels est donc le nombre de lignes renvoyées par
la commande \com{getent} :
\begin{shellcom}
  ¢usercom«getent passwd | wc -l»
     277
\end{shellcom}
\index{compter}

Pour obtenir la liste des utilisateurs dont le groupe principal est un
groupe particulier, par exemple le groupe numéro $1300$, on peut agir
comme suit :
\begin{shellcom}
  ¢usercom«getent passwd | grep -w 1300 | cut -d':' -f5»
  MERLE Geraldine
  ESCOFFIER Carole
  GACON-LOCATELLI Anne
  BOYAUD Xavier
  SIGUIER Vincent
  LASNIER Christophe
  PEROCHE Jerome
  «¢normalfont... etc ...»
\end{shellcom}
on fait une recherche de toutes les lignes contenant la chaîne 1300
(ce qui n'est pas exactement ce que l'on cherche, je vous l'accorde,
mais lisez la suite), et pour ces lignes, on affiche le 5\ieme{} champ
(en précisant que le séparateur est~«:»).  Pour classer par ordre
alphabétique, il suffit de rajouter un tube avec \texttt{sort} :
\begin{shellcom}
  ¢usercom«getent passwd | grep -w 1300 | cut -d':' -f5 | sort»
  ALBERT-GONDRAND Johanne
  BARNEOUD Julien
  BAROQUE Philippe
  ARTOS Romain
  ...
\end{shellcom}
Enfin si on voulait afficher le résultat sous la forme prénom-nom, on
pourrait rajouter un « petit » \com{awk} :
\begin{shellcom}
  ¢usercom«getent passwd | grep 1300 | cut -d':' -f5 | sort | »
  ¢usercomwmp«awk '{print $2" "$1}'»
  Johanne ALBERT-GONDRAND
  Julien BARNEOUD
  Philippe BAROQUE
  Romain BARTOS
  ...
\end{shellcom}

\begin{note}
  Les lecteurs attentifs auront sans doute noté que le filtre
  \texttt{grep 1300} pourra sélectionner une ligne contenant la chaîne
  1300, même s'il ne s'agit pas du numéro de groupe. Pour remédier à
  ce problème on pourrait utiliser à la place du \texttt{grep 1300} :
\begin{verbatim}
   gawk -F: '$4==1300{print $5}'
\end{verbatim}
  \continuenota qui aurait tout de même quelque peu surchargé notre exemple à
  vocation pédagogique (l'option \texttt{F} indique à \com{awk} le
  séparateur de champ des données en entrée, la variable \texttt{OFS}
  pour \emph{output field separator})...
\end{note}

\subsection{Envoyer des mails}
\index{mail!envoyer}

Pour faire ce qu'on appelle du \anglais{mailing} (envoyer le même
message à plusieurs utilisateurs), on peut créer deux fichiers :
\begin{itemize}
\item \texttt{destinataires} contenant les adresses électroniques des
  destinataires, et :
\item \texttt{message} contenant le message à envoyer.
\end{itemize}
On peut créer \texttt{destinataires} manuellement ; on peut également
le créer automatiquement, par exemple les destinataires « groupe 1300
» pourraient être construits comme suit :
\begin{shellcom}
  ¢usercom«getent passwd | awk -F: '$4 == 1300' { print $1 }' > destinataires»
\end{shellcom}

L'envoi des messages est réalisé grâce à une boucle \texttt{for} :
\begin{shellcom}
  ¢usercom«for U in $(cat destinataires); do\»
  ¢usercomwmp«mail -s "salut" $U < messages»
  ¢usercomwmp«done»
\end{shellcom}
%$
Si la commande \com{mail} de votre système est capable d'envoyer des
messages à plusieurs utilisateurs à la fois, vous pourriez également
écrire :
\begin{shellcom}
  ¢usercom«mail -s "salut" $(getent passwd | awk -F: '$4 == 1300'{print $1})»
\end{shellcom}
%$

\subsection{Estimer l'occupation de certains fichiers}
\index{fichier!occupation}

Pour estimer la taille d'un certain type de fichier (comme les
images, les fichiers PostScript, ...) on peut procéder comme suit :
tout d'abord rechercher ces fichiers avec la commande \com{find} :
\begin{shellcom}
  ¢usercom«find ~/ -name "*.ps"»
  /home/equipe/lozano/LaTeX/test/test.ps
  /home/equipe/lozano/LaTeX/local/local.ps
  /home/equipe/lozano/LaTeX/local/local.2up.ps
  ...
\end{shellcom}
qui donne la liste de tous les fichiers PostScript (extension
\texttt{ps}) dans la zone de l'utilisateur. La version \gnu{} de
\com{find} permet également d'afficher les caractéristiques des
fichiers listés grâce à une option tout à fait analogue au
\texttt{printf} du C :
\begin{shellcom}
  ¢usercom«find ~/ -name "*.ps" -printf "%p %k\n"»
  /home/equipe/lozano/LaTeX/test/test.ps 6
  /home/equipe/lozano/LaTeX/local/local.ps 1850
  /home/equipe/lozano/LaTeX/local/local.2up.ps 1880
  ...
\end{shellcom}
%$
la chaîne de format du \texttt{printf} utilise \texttt{\%p} pour le
nom du fichier, et \texttt{\%k} pour sa taille arrondie en kilo-octets
(voir l'aide de la commande \com{find}).
Grâce à un  tube avec \texttt{awk} on peut afficher les fichiers dont
la taille est supérieure à 1 méga-octet :
\begin{shellcom}
  ¢usercom«find ~/ -name "*.ps" -printf "%p %k\n" |»
  ¢usercomwmp«awk '$2 > 1024 {print $1" "$2}'»
  /home/equipe/lozano/LaTeX/local/local.ps 1850
  /home/equipe/lozano/LaTeX/local/local.2up.ps 1881
  /home/equipe/lozano/LaTeX/util/local.ps 1855
  /home/equipe/lozano/install/lettre/doc/letdoc2.ps 1385
  ...
\end{shellcom}
%$
On peut également obtenir la taille totale de ces fichiers :
\begin{shellcom}
  ¢usercom«find ~/ -name "*.ps" -printf "%k\n" | »
  ¢usercomwmp«awk '{taille+=$1}END{print taille}'»
  52202
\end{shellcom}
%$
Pour finir si on voulait effacer tous ces fichiers, ou seulement ceux
dont la taille est supérieure à un seuil donné, il suffirait de
procéder comme suit :
\begin{shellcom}
  ¢usercom«find ~/ -name "*.ps" | xargs rm -f»
\end{shellcom}
Sans argument particulier, \com{find} affiche la liste des fichiers
répondant au critère. La commande \com{xargs} passe en argument de la
commande qui la suit (ici \texttt{rm -f}) le flux qu'elle reçoit en
entrée. Donc ici on va exécuter \verb+rm -f+ avec comme argument tous
les fichiers renvoyés par la commande \com{find}.

Voici un autre exemple d'utilisation de la commande \com{xargs} :
supposons qu'on veuille chercher dans quel fichier \emph{include} (les
fichiers entête du langage~C) est défini l'appel système
\texttt{gethostbyname}\footnote{Encore un exemple farfelu lorsqu'on
  sait que \texttt{man gethostbyname} donne l'information recherchée.}.
Pour ce faire on va chercher tous les fichiers dont l'extension est
\texttt{.h} du répertoire \verb+/usr/include+ et de ses
sous-répertoires :
\begin{shellcom}
  ¢usercom«find /usr/include -name "*.h"»
  /usr/include/utempter.h
  /usr/include/apm.h
  /usr/include/ansidecl.h
  /usr/include/bfd.h
  ...
\end{shellcom}
Pour l'ensemble de ces fichiers on va utiliser \com{grep} pour
chercher la chaîne de caractères \texttt{gethostbyname} :
\begin{shellcom}
  ¢usercom«find /usr/include -name "*.h" | xargs grep -w gethostbyname»
  /usr/include/netdb.h:extern struct hostent *gethostbyname __P 
  ((__const char *__name));
  /usr/include/tcpd.h:#define gethostbyname fix_gethostbyname
\end{shellcom}
Cette idée peut être utilisée pour chercher une chaîne dans un ensemble
de fichiers. Un autre forme juste pour confirmer l'épigraphe de la
préface :
\begin{shellcom}
  ¢usercom«grep --include='*.h' -rw gethostbyname /usr/include»
\end{shellcom}
%$
demande à grep de faire une recherche récursive (\texttt{-r}) dans le
répertoire spécifié en dernier argument (\verb+/usr/include/+) en
n'examinant que les fichiers dont le nom finit par \verb+.h+ (ceci est une
« exclusivité » \gnu{})...
\section*{Conclusion}
\label{sec-unix-conclusion}

Vous êtes maintenant armés  pour étudier les chapitres
suivants respectivement consacrés à la communication via le réseau, au
développement de programmes et la configuration de son environnement.
Le chapitre traitant du développement inclut une partie non
négligeable présentant le shell en tant que langage de programmation
en mode non interactif.  De manière générale le présent chapitre donne
un aperçu de la « philosophie » d'\unix{} et doit vous permettre
d'appréhender différents outils et logiciels que vous serez
susceptible de rencontrer.


%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "../framabook"
%%% End: 

