Page suivante Page précédente Table des matières

7. Combiner les outils

Maintenant, supposons que nous sommes sur un gros système BBS avec des dizaines d'utilisateurs connectés. Le service de gestion demande à l'administrateur système d'écrire un programme qui générera une liste triée des utilisateurs. De plus, même si un utilisateur est connecté plusieurs fois, son nom ne devra apparaître qu'une fois.

L'administrateur système pourrait s'installer avec sa documentation système et écrire un programme C qui fasse cela. Cela demanderait sans doute quelques centaines de lignes de code et environ deux heures pour l'écrire, le tester et le déboguer. Cependant, connaissant sa boîte à outils logiciels, il commence par générer simplement une liste des utilisateurs connectés:

$ who | cut -c1-8
arnold
miriam
bill
arnold

Ensuite, il trie cette liste:

$ who | cut -c1-8 | sort
arnold
arnold
bill
miriam

Enfin, il passe la liste triée à travers uniq, pour éliminer les doublons:

$ who | cut -c1-8 | sort | uniq
arnold
bill
miriam

En fait la commande sort possède l'option -u qui fait la même chose qu'uniq. Mais uniq a d'autres utilisations pour lesquelles on ne peut pas utiliser sort -u.

L'adsys place ce tube dans un script shell, et le rend disponible pour tous les utilisateurs du système:

$ cat > /usr/local/bin/listusers
who | cut -c1-8 | sort | uniq
^D
$ chmod +x /usr/local/bin/listusers

Il y a quatre points importants à noter. Premièrement, avec seulement quatre programmes, sur une ligne de commande, l'adsys a pu économiser environ deux heures de travail. De plus, le tube shell est tout aussi efficace que le serait le programme C, et il est bien plus efficace en terme de temps programmeur. Le temps ``humain'' est bien plus couteux que le temps machine, et dans notre société moderne où ``il n'y a jamais assez de temps pour tout faire'', économiser deux heures de temps de programmeur n'est pas négligeable.

Deuxièmement, il est important d'insister sur le fait qu'avec une combinaison des outils, il est possible de réaliser un travail bien précis qui n'avait jamais été imaginé par les auteurs des différents outils.

Troisièmement, il est intéressant de construire votre tube par étapes, comme nous l'avons fait ici. Cela permet de voir les données à chaque étape du tube, ce qui aide à se persuader que l'on utilise correctement les outils.

Finalement, en mettant le tube dans un script shell, les autres utilisateurs peuvent utiliser votre commande, sans avoir à se rappeler la plomberie sophistiquée que vous avez mise en oeuvre pour eux. Pour ce qui concerne la façon de les exécuter, les scripts shell et les exécutables sont absolument identiques.

Après cet exercice d'échauffement, nous allons étudier deux autres tubes plus compliqués pour lesquels nous devons introduire deux nouveaux outils.

Le premier est la commande tr pour ``transliterate'' (transcrire). Elle permet de remplacer des caractères par d'autres. Normalement, elle est utilisée pour des choses du genre transformer les majuscules en minuscules:

$ echo CeT ExEmPlE A Des MaJuscUles et DES minuscules ! | tr '[A-Z]' '[a-z]'
cet exemple a des majuscules et des minuscules !

Il y a plusieurs options intéressantes:

  1. -c travaille sur le complément des caractères listés, i.e. les opérations s'appliquent aux caractères qui ne sont pas dans le premier ensemble
  2. -d remplace les caractères répétés par un seul caractère.
  3. -s élimine les caractères doublons en sortie.

Nous utiliserons ces trois options dans un moment.

L'autre commande est comm. Elle prend en entrée deux fichiers triés et affiche les lignes de ces fichiers en trois colonnes. Les colonnes en sortie contiennent respectivement les lignes uniques au premier fichier, les lignes uniques au deuxième fichier et les lignes communes aux deux. Les options -1, -2 et -3 omettent les colonnes respectives (ce n'est pas très intuitif, il faut un peu de temps pour s'y habituer). Par exemple:

     $ cat f1
     11111
     22222
     33333
     44444
     $ cat f2
     00000
     22222
     33333
     55555
     $ comm f1 f2
             00000
     11111
                     22222
                     33333
     44444
             55555

Un tiret pour nom de fichier dit à comm de lire l'entrée standard au lieu d'un fichier classique.

Maintenant nous sommes prêts à construire un tube un peu complexe. La première application est un compteur de fréquence de mots qui peut aider un auteur à déterminer s'il utilise trop certains mots.

La première étape est de transformer toutes les majuscules en minuscules (ou l'inverse), vu que ``Le'' et ``le'' sont le même mot pour notre compteur.

$ tr '[A-Z]' '[a-z]'  < whats.gnu | ...

Dans l'étape suivante on se débarrasse de la ponctuation. Les mots entre guillemets ou pas doivent être traités de la même manière. Le plus simple est de virer toute la ponctuation.

$ tr '[A-Z]' '[a-z]'  < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' | ...

La seconde commande tr opère sur le complément des caractères listés, qui sont les lettres, les chiffres, le soulignement et le blanc. Le \012 représente le caractère newline (fin de ligne) qu'il ne faut pas supprimer. Notons que le caractère ASCII TAB devrait sans doute aussi être inclus dans un exemple réel.

Nous avons alors des données consistant de mots séparés par des espaces. Ces mots ne contiennent que des caractères alphanumériques (plus le soulignement). La prochaine étape est de séparer les données de façon à avoir un mot par ligne. Cela rend l'opération de comptage bien plus facile, comme nous le verrons bientôt.

$ tr '[A-Z]' '[a-z]'  < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | ...

Cette commande change les espaces en retour chariot. L'option -s élimine les répétitions en sortie, ce qui nous évite d'avoir des lignes blanches (le > est l' ``invite secondaire'' du shell, qui apparaît quand le shell se rend compte que vous n'avez pas fini de taper toute votre commande).

Nous avons maintenant des données sous la forme un mot par ligne, sans ponctuation, tout en minuscule. Nous sommes prêts à compter le nombre d'occurences de chaque mot:

$ tr '[A-Z]' '[a-z]'  < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort | uniq -c

Ce qui nous donne quelque chose du genre:

60 à
8 allo
2 allocation
12 allouette
...

Cette sortie est triée par mot, pas par nombre d'apparitions ! Nous voulons avoir le mot le plus utilisé en premier. Heureusement, c'est facile à faire. La commande sort comprend deux autres options:

  1. -n fait un tri numérique (au lieu d'ASCII)
  2. -r renverse l'ordre du tri

Le tube final ressemble à ceci:

$ tr '[A-Z]' '[a-z]'  < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort | uniq -c | sort -nr
156 le
60 à
58 microbeurk
...

Houah ! Cela fait beaucoup de choses à digérer. Mais les principes sont toujours les mêmes. Avec six commandes, sur deux lignes (en fait une ligne coupée pour la présentation), nous avons créé un programme qui fait quelque chose d'intéressant et d'utile, en bien moins de temps qu'il nous aurait fallu pour écrire un programme C qui fasse la même chose.

Une modification mineure dans le tube ci-dessus peut nous donner un vérificateur d'orthographe simple ! Pour déterminer si vous avez écrit correctement un mot, il suffit de le chercher dans un dictionnaire. S'il n'y est pas, alors il est probablement incorrect. Donc, nous avons besoin d'un dictionnaire. Si vous avez la distribution Slackware de Linux, vous avez le fichier /usr/lib/ispell/ispell.words, qui est une liste triée de 38400 mots. Sur une Debian les listes de mot sont dans /usr/share/dict et dans /usr/dict sur une RedHat.

Maintenant, comment comparer notre fichier avec le dictionnaire ? Comme précédemment, nous générons une liste triée de mots, un par ligne:

$ tr '[A-Z]' '[a-z]'  < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort -u | ...

Il nous faut une liste des mots qui ne sont pas dans le dictionnaire. C'est là que nous utilisons la commande comm:

$ tr '[A-Z]' '[a-z]'  < whats.gnu | tr -cd '[A-Za-z0-9_ \012]' |
> tr -s '[ ]' '\012' | sort -u |
> comm -23 - /usr/lib/ispell/ispell.words

Les options -2 et -3 éliminent les lignes qui ne sont que dans le dictionnaire (le second fichier) et les lignes qui sont dans les deux fichiers. Les lignes qui ne sont que dans le premier fichier (l'entrée standard, notre flux de mots), sont des mots qui ne sont pas dans le dictionnaire. Ils sont de bons candidats pour des erreurs d'orthographe. Voila le premier jet d'un vérificateur orthographique pour Unix.

Quelques autres outils qui méritent d'être mentionnés.

La philosophie des outils logiciels a également adopté le précepte suivant: ``Laissez quelqu'un d'autre faire le plus dur''. C'est à dire prenez quelque chose qui vous donne la plupart de ce dont vous avez besoin, puis triturez les données jusqu'à ce qu'elles soient sous la forme que vous cherchez.

En résumé:


Page suivante Page précédente Table des matières