TetaTricks/chap/scripting.tex

204 lines
7.2 KiB
TeX

\chapter{scripting}
Qu'est-ce que le \textsl{scripting} ?
C'est l'art de coller entre eux divers outils logiciels
afin de réaliser une tache donnée.
Le "collage" est fait par un \textbf{script},
lequel est un fichier texte, écrit dans divers
langages (plus ou moins spécifiques) et qui sera lu et exécuté
par un interpréteur.
Je ne vais pas revenir sur l'art du shebang, si vous ne
le connaissez pas, c'est expliqué page \pageref{shebang}.
En bref, c'est la manière dont le script sélectionne
le bon interpréteur lors de son lancement.
Quels sont les langages courrament utilisés pour faire du
script ? Il y a une bonne poignée de réponses à ça.
La première étant souvent « Use The Shell ! »,
et la seconde « tu as songé à awk ? »,
ce qui est tout aussi sensé.
Le shell étant dégrossi page \pageref{chap:shell},
passons directement au langage Awk.
% ===============================================================
\section{Awk} \index{Awk}
Awk est un langage de programmation crée
par Aho, Kernighan, and Weinberger%
\footnote{The AWK Programming Language, Alfred V. Aho,
Brian W. Kernighan, Peter J. Weinberger, Addison-Wesley, 1988.
ISBN 0-201-07981-X. }.
Son originalité vient du fait qu'il a été conçu principalement
pour traiter ligne par ligne des fichiers tabulés.
C'est son domaine de prédilection, autant en profiter.
Quelques \textsl{oneliners} de bon aloi pour vous donner
une idée des possibilités du langage\dots
\begin{verbatim}
cat { print $0 } or just 1
grep pattern /pattern/
head -n 5 NR <= 5
cut -f1 { print $1 }
tr a-z A-Z { print toupper($0) }
sed 's/hi/ho/g' gsub(/hi/,"ho")
wc -l END { print NR }
\end{verbatim}
Vous pouvez déja constater une certaine élégance portée par une
grande simplicité logique, une sorte d'axiome de base, de
méthologie très \textsc{kiss} :
on va procéder en quelque sorte « demi-dalle par demi-dalle ».
% ===============================================================
\subsection{Un exemple simple}
À partir d'un jeu de données structurées, nous allons générer un
fichier \texttt{.inc} contenant la description en
SDL\index{SDL}\footnote{SDL: Scene Description Language}
d'un objet pour Povray\index{Povray}.
Un cas d'école : un élément par ligne, les valeurs sont séparées par
des espaces et/ou des tabulations.
\lstinputlisting[]{code/awk/dataset}
Ces données sont les coordonnées \textsc{x,y,z} et le rayon d'une
petite collection de bubulles. Pratiquement, une représentation
sommaire d'une sphère de povray.
Le fichier à générer est en trois partie : l'en-tête, la liste
des bubulles\index{bubulle} et l'en-pied, ce qui est bien raccord
avec la structure de déroulement d'un script Awk, comme nous allons
le voir ici-même~:
\lstinputlisting[]{code/awk/mkunion.awk}
La première ligne est pour le traditionnel
\textsl{shebang}\index{shebang},
avec l'option \texttt{-f} pour que le contenu du script soit lu
par l'interpréteur Awk dès son lancement.
Ensuite, nous trouvons trois blocs délimités par des accolades,
deux d'entre eux étant précédés d'une « instruction ».
Le premier bloc, avec le mot-clef \textsc{BEGIN}, est exécuté
avant la lecture de la première ligne des données en entrée.
Ce qui est le bon moment pour initialiser des variables.
Nous l'utilisons pour créer l'en-tête d'un descripteur
d'objet pour Povray.
Le second bloc (sans label) est exécuté pour chaque ligne lue.
Et c'est ici que nous trouverons la magie.
La ligne lue depuis l'entrée a été découpée selon le
séparateur FS, et les tranches sont connues sous les noms
de \$1, \$2, ... \$N (la ligne entière étant \$0)
que nous utilisons dans la génération de la bubulle.
Et le troisième bloc (\textsc{END}) sera exécuté à la fin, après
la lecture et le traitement du dernier enregistrement,
qui est dans notre cas la dernière ligne.
Et à l'exécution~:
\begin{verbatim}
tth@redlady:~/Devel/TetaTricks/code/awk$ ./mkunion.awk < dataset
#declare Bubulles = object
{
union {
sphere { <17.000000, 9.000000, 4.000000>, 1.500000 }
sphere { <11.000000, 0.800000, 2.300000>, 0.989000 }
sphere { <0.000000, 0.000000, 0.000000>, 1.000000 }
}
}
// 3 bubulles
\end{verbatim}
% ===============================================================
\subsection{Mais ce n'est pas tout !}
Ce premier exemple nous a montré comment, avec quelques
lignes de code facile à comprendre, transformer des
données et faire quelques calculs avec ces données.
Nous avons vu le découpage de la ligne d'entrée, mais
ce n'est pas tout, je suis passé très vite sur ce sont
ces "labels" dont j'ai parlé.
Ce ne sont absolument pas des labels, ni des mot-clefs,
mais quelque chose de bien plus puissant.
C'est le mécanisme qui permet de sélectionner les lignes
sur lesquelles nous voulons appliquer un traitement.
Nous avons déja vu \textsc{BEGIN} et \textsc{END},
pour lancer du code avant-le-début et/ou après-la-fin.
Mais nous pouvons mettre des bouts de code donnant un résultat
booléen, oui/non, qui conditionnera l'exécution du
bloc de code ainsi
préfixé\footnote{J'ai l'impression de pas être très clair ?},
ce qui nous donnera des possibilités de filtrage
surpuissantes.
Par exemple, nous ne voulons pas de bubulle trop
petite, il suffit de précéder le second bloc de l'exemple
déja vu par un test sur la taille qui est dans
le quatrième champ du fichier d'entrée~:
\begin{verbatim}
$4 > 0.999 {
printf(" sphere { <%f, %f, %f>, %f }\n", \
$1, $2, $3, $4 )
}
\end{verbatim}
Vous comprenez maintenant les deux plus importantes choses
que l'on peut trouver dans Awk (/me fan).
XXX to be continued \index{XXX}
% ===============================================================
\subsection{Définir une fonction}
Bien, nous savons maintenant générer des bubulles avec un
filtrage sur la taille, mais nous voudrions maintenant procéder
à des calculs sur les données que nous lisons.
Deux choix s'offrent à nous, soit faire ça directement dans
le deuxième bloc, soit utiliser un sous-programme dédié.
Pour des raisons aussi bien didactiques que de bon sens,
nous allons opter pour la seconde solution.
Mais voyons d'abord la syntaxe d'une fonction, avec en prime
une petite astuce: si vous voulez utiliser Awk dans devoir
lui fournir de données en entrée, c'est simple, mettez tout
dans le bloc \textsc{BEGIN}.
\lstinputlisting[]{code/awk/demo-func.awk}
\begin{verbatim}
tth@redlady:~/Devel/TetaTricks/code/awk$ ./demo-func.awk
---- demo fonction ----
sin(1.000000) = 0.841471
distance from center = 0.707107
\end{verbatim}
La deuxième fonction demande quelques explications, puisqu'elle
met en œuvre un « awkisme » de bon aloi.
En effet, Awk ne connait pas les variables locales à une fonction,
c'est-à-dire connues seulement à l'intérieur de celle-ci.
% ===============================================================
\subsection{Les variables}
Et puisque on en parle...
% ===============================================================
\subsection{Pattern filtering} \index{pattern}
Wesh, regexp.
% ===============================================================