2.7 Programmation de commandes
La programmation pour
sh est semblable à toute programmation.
Chaque problème nécessite une analyse puis l'élaboration d'une méthode
de résolution et enfin l'adaptation d'icelle aux moyens que procure le langage
cible.
Considérant que le langage de
sh est suffisament proche d'autres langages courants, nous ne
donnerons que trois
exemples commentés illustrant diverses techniques classiques et quelques
autres possibilités non mentionnées de
sh.
2.7.1 Du beau courrier
mail est un utilitaire permettant de lire ou écrire des messages.
Pour lire son courrier il suffit de commander
mail
mail inspecte la boîte à lettre de l'usager (qui est en
général le fichier /usr/spool/mail/$USER)
et permet de consulter, détruire, sauvegarder les messages qui s'y trouvent
(cf. .profile).
Pour envoyer un message, la commande sera
mail destinataire1 destinataire2 ...
mail se comporte comme un filtre c'est-à-dire qu'il considère
son flux d'entrée comme la lettre à expédier.
Il le lira donc jusqu'à déceler une fin de fichier (cf. D).
Ce comportement, s'il est simple, n'est cependant guère
optimal lorsqu'on désire écrire une belle lettre.
On use alors de
mail destinataire1 destinataire2 ... <lettre}
où
lettre sera un fichier que l'on aura composé, par exemple, sous éditeur de textes.
Voici le programme
#
# Usage : mail destinataire ...
#
MAILER=/bin/mail # le véritable mail !!!
if test $# = 0
then # S'il n'y a pas d'argument, c'est une simple
# lecture du courrier
$MAILER
else # Préparer la forme de la belle lettre
cat $HOME/.mailproto >/tmp/mail$$ 2>/dev/null
# L'écrire
${ED-/bin/ed} /tmp/mail$$
# L'envoyer avec copie pour soi
$MAILER $USER $* </tmp/mail$$
# Nettoyer son passage
rm /tmp/mail$$
fi
Les remarques qui suivent éclairent ce programme.
-
MAILER est une variable de
sh qui contient le nom du véritable utilitaire de courrier.
Il est nécessaire que ce soit le nom complet
afin qu'il n'y ait nulle ambiguïté
sur lui.
En effet mettre
mail tout seul renverrait à la commande même que nous explicitons et ferait
ainsi boucler l'interprète, puisque
mail serait recherché avec les mêmes règles qui l'ont fait trouver !
L'intérêt est que cette nouvelle commande pourra se nommer comme
l'ancienne qu'elle exploite :
mail!
-
$# est une variable dynamique prédéfinie de
sh.
Sa valeur est une chaine de caractères représentant (en base décimale)
le nombre d'arguments d'appel à la commande courante.
Deux cas sont à distinguer suivant que
mail est invoqué sans argument ou avec des destinataires.
-
test est un programme particulier dont le rôle est de rendre
un code de retour égal à zéro ou un, c'est-à-dire (respectivement)
Vrai ou Faux .
test détermine sa valeur après analyse de ses arguments d'appel.
Ceux-ci sont ici :
une chaine de caractères obtenue par valeur de
$#,
la chaine de caractère formée du seul signe
=,
la chaine de caractères formée du caractère
0.
Ces trois arguments spécifient à
test d'effectuer une comparaison dont le résultat sera exploité par
if.
-
On utilise, pour préparer sa lettre, un fichier temporaire que l'on place
tout naturellement dans le répertoire
/tmp.
$$ est une autre variable prédéfinie de
sh dont la valeur est la chaine de caractères représentant le numéro de
la tâche qui exécute
mail.
On garantit ainsi que deux utilisateurs différents utilisant ensemble
mail ne se gênent point puisqu'ils travailleront l'un comme l'autre avec
des fichiers temporaires différents.
-
Le fichier temporaire est créé par recopie d'un fichier particulier
de l'utilisateur nommé
.mailproto et situé dans son répertoire
origine (La plupart des programmes qui ont besoin de données valables quelque soit
l'endroit d'où ils sont appelés, placent ces données dans des fichiers
invisibles (dont le nom débute par un point) situés sous le répertoire
origine.
Par exemple,
emacs,
msgs,
winnie,
sh ...)
($HOME)
Le flux d'erreur est détourné sur
/dev/null afin que si erreur il y a (la plus probable étant que
.mailproto n'existe pas) elle soit invisible.
-
La lettre contient alors un fond (par exemple, une entête, un colophon ...)
modifiable par un éditeur de texte.
Celui-ci est déterminé par la valeur de la variable
ED qui contient le nom de votre éditeur préféré.
${ED} est équivalent à
$ED mais
${ED-/bin/ed} permet de spécifier que si la variable
ED n'existe pas, ce sera l'éditeur standard d'UNIX
qui sera utilisé.
-
$* est une autre variable dynamique prédéfinie de
sh.
Elle équivaut à la suite des arguments
$1 $2 ...$9.
Elle représente donc ici la liste des destinataires de la lettre.
-
Enfin lorsque vous sortirez de l'editeur, votre lettre sera expédiée et
le fichier temporaire effacé.
2.7.2 Fouiner
Le programme qui suit permet d'extraire tous les fichiers lisibles
accessibles depuis un répertoire.
Il est formé d'un longue théorie de cascades.
#
# Usage : fouiner répertoire
#
# Produire tous les noms accessibles depuis le répertoire argument
find $1 -print |
# ... puis pour chacun ...
while read f
do
file $f # déterminer son type
done |
# n'en retenir que ceux qui sont des textes
sed -n -e '/ text/s!: .*$!!p' |
# et pour chacun s'enquérir de sa taille
while read f
do
wc -c $f
done |
# Trier par ordre croissant de taille
sort - +0n +1d |
# Eliminer les blancs superflus ...
sed -e 's!^ *!!' |
# puis regrouper sur une seule ligne les fichiers de même taille ...
if read n f
then while read p g
do
if test $n = $p
then f="$f $g"
# préfixé de la commande "unique"
else echo unique $f
f="$g"
n=$p
fi
done
echo unique $f
fi |
# puis exécuter
sh
fouiner use du programme
unique qui suit :
#
# Usage : unique fichiers...
#
# Pour tous les fichiers arguments
for fichier
do
# echo On compare le fichier $fichier
shift
for f in $*
do
# echo avec $f
if cmp -s $fichier $f
# On oublie fichier si semblable à l'autre
then exec unique $*
fi
done
# mais on le sort s'il est différent de tous les autres.
echo $fichier
done
Encore quelques remarques.
-
La première cascade pourrait, logiquement et plus simplement, s'écrire :
file `find $1 -print`
en usant du métacaractère accent grave (backquote)
qui convertit un flux (le résultat d'une commande) en une liste
d'arguments.
Le malheur est que
sh ne peut manipuler un trop grand nombre d'arguments (surtout s'il
dépasse la centaine).
Il faut donc les traiter par lots et le plus simple est de les considérer
un par un.
-
find est un utilitaire qui permet de rechercher un nom dans une arborescence.
On peut associer à cette recherche des conditions sur la forme du nom,
son type (fichier, répertoire ...), sa dernière date de modification,
etc ...Ici comme aucune condition ne restreint le parcours, tous les noms sont
imprimés sur des lignes différentes.
-
La phrase :
while read f
do
file $f
done
permet de lire cet ensemble de lignes et d'appliquer l'utilitaire
file à chacun des fichiers mentionnés.
read f affecte à la variable
f le nom lu qui est alors soumis en argument à
file.
-
file est un autre utilitaire qui inspecte le début d'un fichier et tâche d'en
déduire son type.
Il reconnait les répertoires, les programmes exécutables et tente de
déterminer le langage dans lequel sont écrits les fichiers
contenant des textes.
Le mot
text apparait alors si le fichier semble lisible.
Par exemple,
$ file *
cnv: command text
cupple: english text
cupple.c: C text
cupq: pure executable not stripped
date: pure executable stripped
db: tbl, eqn or roff text
dir: directory
Les lignes produites contiennnent le nom du fichier suivie de
: et d'une tabulation (I) puis d'un ensemble de mots caractérisant
la nature du fichier.
On ne souhaite retenir que ceux de type texte aussi filtre-t'on
le flux de sortie pour ne laisser passer que les lignes où figure le mot
text.
On supprime en outre tout ce qui suit le nom du fichier, désormais inutile.
-
sed (pour
Stream EDitor)
est un filtre apparenté à l'éditeur
ed,
mais, à rebours d'un éditeur de texte classique où le fichier à traiter
est connu et la suite des commandes à effectuer dessus inconnue, ici, la
suite des commandes est connue.
Chaque ligne du flux entrant est lue et successivement confrontée à
toutes les commandes, modifiée en conséquence puis émise dans le flux
sortant.
Une seule commande permet ici d'effectuer le filtrage :
/ text/s!: .*!!p
ce qui se lit simplement ainsi :
- / text/
La commande qui suit ne s'applique qu'aux lignes
contenant la suite des lettres « text » (le
blanc qui précède autorise les noms de fichiers contenant les lettres
« text »).
- s
Substituer ...
- : .*
...une séquence de lettres débutant par le caractère deux-points
suivi d'une tabulation (peu visible ici) suivie d'une séquence de
caractères quelconques (le point
représente n'importe quoi,
l'étoile
déclare qu'on peut trouver l'objet précédent un nombre de fois
quelconque.
C'est la notation propre aux expressions régulières de Kleene)...
- $
...contrainte à se terminer en fin de ligne.
- !!
La séquence de lettres, si trouvée, est remplacée par rien,
c'est-à-dire effacée.
! est ici le séparateur des deux parties de la substitution.
- p
Enfin imprimer !
-
Le flux ne comporte donc plus que des noms de fichiers lisibles.
On pourrait s'arrêter là mais en fait de nombreux fichiers sont
semblables (de même contenu).
Ce qui suit permet d'éliminer ces doublons.
-
Pour chacun de ces fichiers on extrait sa taille grâce à
wc (pour
Word Count)
avec l'option
-c car on ne désire que la seule taille en caractères.
$ wc -c *
1256 u0
17006 u1
3483 u2b
-
On trie le flux engendré grâce à
sort (numériquement sur le premier champ et alphabétiquement sur le second)
et enfin on regroupe ensemble tous les fichiers ayant même taille.
Il est alors évident que deux fichiers de taille différentes ne peuvent
être semblables aussi restreindra-t'on les comparaisons (coûteuses
puisqu'effectuées octet par octet) qu'entre fichiers de tailles identiques.
-
Le script de regroupement
if read n f
then while read p g
do
if test $n = $p
then f="$f $g"
# préfixé de la commande "unique"
else echo unique $f
f="$g"
n=$p
fi
done
echo unique $f
fi
s'il prend le flux
128 /u/qnc/bouquin/u0
128 /u/qnc/bouquin/save/u0
128 /etc/group
129 /u/scratch/#123
130 /usr/bin/.emacs_219
produira
unique /u/qnc/bouquin/u0 /u/qnc/bouquin/save/u0 /etc/group
unique /u/scratch/#123
unique /usr/bin/.emacs_219
-
Enfin la commande
unique permet d'éliminer les doublons pouvant figurer dans ses arguments.
Il suffit d'exécuter le flux sortant pour que le tour soit joué !
Si vous désirez lire tout ce qui est lisible (et accessible, mais la
plupart des choses le sont sous
UNIX)
il vous suffira d'ajouter au programme précédent la cascade suivante
où chaque fichier pourra être affiché sur votre écran grâce à
l'utilitaire
more.
while read f
do
more $f
done
more est un programme bien utile qui découpe un fichier en pages d'un nombre de
lignes ajusté à l'écran que vous employez pour le lire.
Sous
more vous disposez de toute latitude pour lire posément, revenir en arrière
ou chercher des chaines de caractères.
Bonne lecture !
2.7.3 Ce livre
Ce livre a été, comme il se doit, entièrement composé sous
UNIX.
La composition est un processus qui dispose des lettres, des phrases, des
paragraphes au sein de pages.
nroff est un formateur de textes effectuant ce travail avec comme cibles des
imprimantes classiques.
Un formateur de textes est un filtre qui traite des fichiers dans
lesquels figurent des textes à composer et des directives de formatage
associées.
Ces directives indiquent les traitements à opérer sur ces textes
(centrer, justifier à gauche et/ou à droite, mise en place de
tabulations, de marges, numérotation de chapitres, élaboration
de la table des matières, changement de polices ...).
Les directives sont repérées par un point en tête de ligne.
Il est possible sous
nroff de créer ses propres directives par assemblage de directives prédéfinies.
Des jeux tout faits et paramètrables existent donc comme
man qui permet de formater le
manuel
de référence, ou encore
me (dû à Eric.P. Allman de l'UCB) ici présentement mis à contribution :
qu'il en soit remercié !
nroff peut lui-même être paramètré, au prix d'une recompilation, en fonction
de l'imprimante visée pour cible.
Tout comme les terminaux intelligents
(cf. De l'intelligence des terminaux), les imprimantes reconnaissent
diverses séquences de caractères ordonnant des actions comme des
déplacements horizontaux ou verticaux, des changements de police ...Par exemple L fait sauter en tête de la page suivante.
Cependant
nroff ne peut être totalement paramètré pour la langue
française en raison des signes diacritiques.
Le programme
typo a donc été créé afin de composer ce livre.
#
# Usage : typo -imprimante fichiers ...
#
TYPO=/u/qnc/TYPO
type="$1"
shift
case X$type in
X-IDS)
cat $TYPO/init.me $TYPO/IDS.me $* |
accent |
nroff - -TIDS -e |
$TYPO/IDS.me ;;
X-FT)
cat $TYPO/init.me $TYPO/FT.me $* |
accent |
nroff - -TFT -e |
$TYPO/FT.me ;;
X-DY)
cat $TYPO/init.me $TYPO/DY.me $* |
accent |
nroff - -TDY -e |
$TYPO/DY.me ;;
*)
exit 1 ;;
esac
typo est une cascade où les fichiers à traiter passent en premier lieu
au travers du filtre
accent.
Le but de ce dernier est de manipuler correctement les signes diacritiques
dont les accents.
Ceux-ci sont, dans les fichiers à formater codés de la façon suivante :
é est écrit e'
à est écrit a`
c// est écrit c//
oe est écrit o//e
etc ...
Ainsi la phrase:
Un été sans noël haï en hâte et en oeuvre
au sein de ce paragraphe s'écrirait :
.pp
Ainsi la phrase
.b
.ce
Un e'te' sans noe:l hai: en ha^te et en o//euvre
.r
au sein de ce paragraphe s'e'crirait :
.pp est une directive de
me stipulant qu'un nouveau paragraphe débute.
.b et
.r basculent l'impression en police grasse (bold) ou normale (roman).
.ce est une directive de nroff qui centre la ligne qui suit
au milieu d'une ligne.
Le résultat de
accent est de transformer tous ces codages en un codage approprié à
nroff.
e` est transformé en \o'e`'
c// est transformé en \o'c,'
o//e est transformé en o\h'-0.2m'e
Pour
nroff,
\o (pour
overstrike)
signifie qu'il lui faut empiler la frappe des caractères qui suivent :
ainsi é est-il obtenu par frappe superposée de la lettre
e et d'un accent aigu.
\h (pour Horizontal)
réalise un déplacement horizontal.
Le « m » est une unité de distance typographique égale à la chasse
de la lettre « m ».
Une fois ces transcriptions effectuées ainsi que diverses autres telles
que l'insertion d'un demi-espace entre le dernier mot d'une phrase
et le point final, etc ...on aboutit au formatage.
Les appels à
nroff sont paramètrés par le type de l'imprimante visée (FT signifie
Fortune Terminal, IDS est l'imprimante PRISM 132, etc ...).
Le fichier à formater est précédé du fichier standard
init.me qui contient tous mes idiotismes généraux et du fichier
FT.me ou
IDS.me où se trouvent tous les paramètres dépendants de l'imprimante cible
(taille du papier notamment).
Le résultat du formatage passe alors au crible
d'un filtre dépendant de la cible, en effet divers travaux sont
encore inachevés.
Un
motsouligné comme celui-ci
est engendré par
nroff comme la séquence mH_oH_tH_.
Chaque caractère est suivi d'un retour arrière puis souligné or, comme
on ne peut généralement superposer des caractères sur un écran et si
un attribut de visualisation « souligné » existe, il faut effectuer la
conversion idoine. Par exemple, pour FT, le résultat sera
\HPmot\IP. Les deux séquences
qui bordent le mot activent puis inactivent le mode souligné
(cf. l'utilitaire ul). En ce qui concerne les polices de
caractères, la police grasse sur DY (Olivetti DY311) est obtenue par
\[E et supprimée par \[R mais
aussi par une fin de ligne.
Lorsqu'un paragraphe de plusieurs lignes est en gras, il est donc
nécessaire de regénérer \[E en tête de chaque ligne
! Enfin en
ce qui concerne les accents, la surimpression conduit à des séquences
que l'on peut quelquefois remplacer par des lettres mieux dessinées.
Sur DY, par exemple, existe sur la marguerite qui l'équipe un « à »
beaucoup plus beau que ce que l'on obtient par aH`.
Tous ces filtres sont des appels à sed.
Malheureusement toutes ces imprimantes, si elles fournissent des brouillons
fort acceptables, n'atteignent pas une qualité typographique suffisante
pour ce livre.
Il faut pour cela utiliser des photocomposeuses ou imprimantes « laser »
et pour cela abandonner
nroff et passer à
troff.
(t pour
typesetter).
troff est compatible de
nroff puisqu'il formate les fichiers de
nroff avec les mêmes conventions.
La composition est un art difficile que ne rend pas simple la
diversité des moyens de reproduction existants.
Les objets d'UNIX
sont des fichiers, des flux et des tâches.
On peut agencer, selon ses goûts et besoins, tous ces divers objets
en schémas plus ou moins compliqués.
Un interprète de commandes n'est qu'un moyen, particulièrement
efficace et simple, de mettre en oeuvre ces schémas en les
traduisant en une succession d'appels au noyau d'UNIX.