Discussion consultée 1451 fois
3 messages
Domos - Apprendre le langage AWK
#1La_Brosse
Bonjour à tous les courageux ;

Le fond du problème :

Le projet Domos repose en grande partie sur les capacités du langage de programmation AWK.
L'interprétation des procédures est effectué par la version GNU Awk (gawk).

J'ai indiqué que ce langage est simple à appréhender, que son usage principal est d'interpréter du texte et de réagir à si ou ça.

Le petit hic, c'est que pour assouvir votre soif d'essayer il faut déjà avoir une machine avec un linux en dedans.

C'est du passé.

Le bout du tunnel :

Pour vous rendre la phase d'essai plus agréable, il existe une solution simple, propre et rapide : cygwin.

Cygwin est un ensemble de programmes et fonctions conçues pour faire usage des outils et programmes Unix sur une machine Windows. Cette magnifique machine à émuler est à l'initiative du consortium RedHat, et le site officiel est à cette adresse.

Et le meilleur, c'est que vous n'avez pas besoin d'installer tout le système pour faire fonctionner gawk !

Et la lumière fût :

J'ai à disposition le programme gawk dans sa version cygwin.
L'archive est un .zip, à décompacter là où l'on souhaite l'utiliser, pas d'installation bizarroïde.

J'ai également deux archives contenant les exercices de prises en main et les sources d'informations.
Ce sont des fichiers tarball Gzip, à décompacter avant usage donc.

Pour commencer à se servir de gawk, il suffit d'ouvrir une console dans le répertoire où vous l'aurez placé.
Lancez la commande "gawk --version" et il affiche son petit nom, la licence GNU GPL.


La suite :
Parmi les exercices, certains emploient des programmes non présent sur votre windows, à moins d'avoir installé tout le système cygwin.

Les fichiers .awk sont lisibles avec n'importe quel outil ouvrant les fichiers textes.

Les exercices sont les bases de compréhension des entrées et sorties. L'origine de ces exercices est dans la grosse notice "GNU Awk Programming", accessible en ligne.

On lance un fichier avec la commande "gawk -f ./geturl.awk"
Le fichier geturl2.awk permet de vérifier l'acquisition de fichiers webs simples. Il sert à vérifier que votre gawk sera capable de lire sur le serveur distant nommé dans le .awk (il n'est plus à jour).
Il sait lire tous les formats (pages htm, xml, txt).

Voici ce qu'il renvoi. Je suis dans une console cygwin.

image/photo



Voilà, désormais plus d'excuse pour ne pas essayer de faire un "Hello Le Monde".


   [ Message modifié par  La_Brosse  le  14-10-2011  à  18:06 ] 
#2La_Brosse
Salut ;

L'ensemble du projet Domos est placé sous licence GNU GPL version 3. Entre autres choses cela autorise les utilisateurs à prendre connaissance des sources des procédures, à les modifier et à partager le fruits de leur efforts.

C'est bien joli tout ça mais après ? Et bien je vous propose d'étudier ensemble le contenu de la plus courante des règles, la règle de base.




Les procédures AWK peuvent figurer autant de lignes de commentaires que nécessaire, cela commence au caractère # et se termine à la fin de ligne.

Le langage AWK est accès sur l'interprétation de commande et le filtrage de contenu de texte. Les gros programmes peuvent être très complexes, et cela ne pose aucun soucis car l'organisation est de mise.
Le listing des commande se découpe en trois blocs principaux :
- l'entête, annoncé par la balise BEGIN ;
- le corps de la procédure commençant après l'entête ;
- la fin, marquant la fin du corps.

L'entête BEGIN s'étend entre accolades { et }, il sert à déclarer et à computer toutes les fonctions et étapes avant de lancer le corps du programme.
En parcourant cette règle domos, vous remarquerez que tout le travail est effectué dans l'entête. Je n'ai pas utilisé de corps de programme.
La raison est simple, les procédures récupèrent le fichier de statut, compare le contenu à des tests listés dans la règle. Il n'y a pas d'utilité à comparer le statut à du texte issu d'un hypothétique autre fichier dont le contenu nous serai en parti inconnu.
L'accolade de fin de l'entête se trouve donc à la fin de la procédure.

L'entête commence donc à la ligne 8 par une série de déclarations qui renseignent des éléments (les constantes) qui serviront ultérieurement.

Citation :
if (URLserveur == "") URLserveur = "192.168.1.21" # l adresse brut du serveur web comme 192.168.1.20
URLstatus = ("http://" URLserveur "/status.xml") # le fichier d'état de la carte
for (k = 0 ; k < 8 ; k++)
{
if (action ~ ("relai" k+1 "_allume")) { commande = ("http://" URLserveur "/preset.htm?led" k "=1") }
if (action ~ ("relai" k+1 "_eteint")) { commande = ("http://" URLserveur "/preset.htm?led" k "=0") }
}
navigateur = ("/usr/bin/elinks " commande)
print "Règle de base version 5\n"
  Citation :


Première ligne, je teste si la constante URLserveur est connue, sinon elle prend la valeur par défaut indiquée (adresse IP privée classique). Cette option permet donc d'indiquer lors de l'appel de la procédure quelle sera la carte relai questionnée ; utile si plusieurs existent sur le réseau.

Seconde ligne, j'emploie l'adresse URLserveur pour désigner l'emplacement du fichier status.xml qui est lisible sur la carte relai IP.
A fin de test vous pouvez désigner un fichier statique tel que celui présent dans le dépôt ; donc même si vous ne possédez pas de carte sous la main.

Lignes 3 à 7, une petite boucle de test parcoure la variable k de 0 à 7, et évalue le contenu de la constante "action" qui est censée être transmise à la procédure lors de son invocation. Si la valeur de k est trouvée dans la constante action, alors la constante "commande" est créditée d'une ligne de texte qui servira à actionner la page preset sur le relai concerné.
Cette gymnastique est nécessaire afin d'employer le numéro 1 pour le premier relai connecté à la carte (qui elle le connait en tant que 0).

A la suite de quoi nous pouvons désigner le programme qui nous est particulièrement utile pour lancer vite et bien la page preset.htm, elinks. Si un autre logiciel navigateur doit être employé, il suffit de l'indiquer ici.

Enfin j'ai placé un petit texte qui s'affiche avec le numéro de version de la règle (ici la 5) et ajoute une nouvelle ligne vide à la suite (le \n).


Les lignes suivantes vont être liées à une fonction magique avec gawk, l'usage de la connectivité réseau et internet pour lire et écrire des fichiers.

Citation :
if (ProxyPort == 0) ProxyPort = 80
HttpService = "/inet/tcp/0/" URLserveur "/" ProxyPort
ORS = RS = "\r\n\r\n"
print "GET " URLstatus " HTTP/1.0" |& HttpService
while ((HttpService |& getline) > 0)
{
  Citation :


Je ne vais pas expliquer dans le détail comment se passe un échange de fichiers sur le réseau avec le protocole HTTP. Mais voici ce que font ses quelques lignes :
- une déclaration de constante ProxyPort si non connue ;
- une déclaration de constante annonçant l'emploi de inet, en mode tcp vers le serveur de la carte relai, au port déclaré ;
- un changement de constante concernant la façon dont les fichiers sont marqués "terminé" ;
- l'envoi d'une requête GET en mode HTTP 1.0, le plus simple, du fichier status.xml, vers le service inet de manière interactive (|& permet à la procédure de lancer inet et de recevoir de lui la réponse dans la foulée jusqu'à la balise de fin) ;
- on lance une boucle ne se terminant pas tant que la réponse du service inet ne sera pas nulle.

Cette dernière boucle va contenir les divers tests qui constituent le gros du travail pour la procédure. Les demandes à inet se faisant dans l'entête, et les tests se faisant "en direct" sur sa réponse, cela explique pourquoi je n'utilise pas le corps de programme dans cette procédure.

Reste à détailler comment tester ce qui nous arrive du net, et le comparer à ce l'on demande à la procédure (les variables test et etat)

Citation :
# Boucle de vérification des valeurs des relais, si la cible est trouvée alors action
for (i = 0; i < 8; i++) {
entree = "<led" i ">" etat
cible = "relai" (i+1)
if ($0 ~ entree && test == cible) print ("lancement de " action) | navigateur
}

# Boucle de vérification des valeurs des entrées, si la cible est trouvée alors action
for (j = 0; j < 4; j++) {
entree = "<btn" j ">" etat
cible = "bouton" (j+1)
if ($0 ~ entree && test == cible) print ("lancement de " action) | navigateur
}
  Citation :


Il y a deux boucles similaires, correspondant à l'état des relais (indiqués par des valeurs 1 ou 0), et à l'état des entrées (boutons indiqués par up ou dn).
Les boucles testent pour toutes les valeurs de i et j si le texte trouvé dans le fichiers statut colle avec le texte du test plus si le "test" indiqué par l'utilisateur colle avec la cible du test de la boucle.
Ici aussi nous avons une gymnastique qui permet de tester le statut avec des mots compréhensibles par l'utilisateur.
Si on souhaite changer les mots "bouton1" par "entree1" lors de l'invocation, c'est ici qu'il faut faire le changement.

Si le test est positif (les deux cibles concordantes trouvées) alors on affiche un texte (lancement de l'action) et on lance le navigateur. Le texte n'est pas censé s'afficher, mais en phase de test, on peut commenter la fin de la ligne (à partir du | ) et profiter de l'affichage de cette ligne à l'écran de la console.

Notez bien l'organisation générale jusqu'ici, avec des indentations successives qui permettent de distinguer les niveaux emboités et les accolades ceinturant un contenu.

Nous en avons bientôt terminé avec cette looooooongue tirade.

Citation :
# printf "%s", $0
}
close(HttpService)
close(navigateur) #referme l appel proprement, mais inutile avec le timer elinks réglé sur really_quit

system ("sleep 2") # patiente largement avec 15 si le réseau est lent
print "ok"

}  Citation :


Avant de fermer la boucle employant la réponse de inet, j'ai placé un commentaire abscons. Cette ligne une fois décommentée permet d'avoir un affichage à l'écran de l'intégralité de la réponse. Là aussi utile en phase d'essais.

A la suite de quoi nous refermons proprement les appels aux autres programmes. Pas nécessairement utile, mais il est toujours préférable de faire ainsi.

Un dernier petit passage avec une attente de 2 secondes, puis l'affichage du glorieux et sempiternel "OK" avant de finir l'entête et la procédure par la même occasion.


Voilà pour cette procédure. Les autres sont du même acabit, avec des variantes portant sur les variables et les constantes à utiliser. et quelques tests adaptés à l'usage que l'on souhaite faire du contenu du fichier status.xml.




   [ Message modifié par La_Brosse  le  14-10-2011  à  18:10 ] 
#3La_Brosse
Salut ;

Je place ici les explications relatives aux divers changements apparus dans les règles du projet.

1 _ Le !shebang :

Les procédures figurent désormais un nombre magique sur leur première ligne, suivit du répertoire où se trouve l'application gawk sur un linux.
Cette ligne permet de rendre le scrript directement exécutable (si son attribut est exécutable pour l'utilisateur courant). On peux donc se passer de l'invocation gawk -f dans la console.

2 _ récupération du status.xml :

La carte IPX répondant mal à la méthode précédente avec Inet, je me suis rabattu vers la méthode simpliste avec wget (prononcé Web-Guette et pas Végéte...).
Accessoirement cela permet aussi de récupérer tout et n'importe quoi en détournant une règle...

Citation :
requete = ("wget " URLstatus " -q -O ./status")
ORS = RS = "\r\n\r\n"
requete |& getline pilou
getline laststatus < "status"  Citation :


Je renseigne d'abord la constante requête avec la commande permettant de lancer wget dans une console. Le -q l'obligeant à la discrétion, le -O lui indiquant le fichier de sortie.
Les séparateurs ORS et RS sont modifié pour que les données soient récupérées sur une seule ligne.
Je lance la requête et indique que la constante pilou récupère le résultat (nul puisque le -q est présent).
Je demande à gawk de remplir une constante avec le contenu du fichier local "status" que wget viens de créer sur place.
J'ai en main le même genre de contenu que celui que fournit inet.

En reprenant la forme, on peut invoquer dans un script awk n'importe quel outil présent sur l'ordinateur. Les unix en général sont vraiment bien fournis à ce niveau, sur un slitaz il suffit de demander "busybox" dans un terminal et on obtiens une liste succincte. Après il suffit de voir leur utilité sur le wikipédia par exemple.

3 _ Les arguments :

Maintenant que les procédures sont correctes, je peux opter pour une simplification partielle de la manière de leur fournir des arguments pour les constantes.
Par exemple dans la règle fil_pilote, un seul paramètre est utile, il s'agit du mode choisit pour le chauffage.

Citation :
erreur = 1
if (ARGC == 1) {
if (mode == "") mode = "eco"
erreur = 0
}
modes = "normal|eco|arret|horsgel|confort1|confort2"
for (i in ARGV) {
if (ARGV[i] ~ modes ) {
mode = ( ARGV[i] )
erreur = 0 }
}  Citation :


La fonction intégrée ARGV[x] permet de récupérer les valeurs de tous les arguments transmit sur la ligne d'invocation.
Par défaut ARGV[0] vaut gawk, premier de la ligne.
Le -f nom_de_la_procédure.awk n'est pas comptabilisé.
Les constantes définies par -v constante=bladibla ne sont pas comptabilisées.
Donc si on invoque le fil pilote en indiquant juste eco ou normal, cette valeur sera ARGV[1].
Si il y en plusieurs, les autres places se remplissent (ARGV[2], ARG[3] etc.).
Je m'évite de la peine, je ne les compte pas, je place une boucle de variable de i pour tous les ARGV définis.

Si il n'y a pas de paramètres libres, le compteur des valeurs (ARGC) vaut 1 (puisqu'il y a gawk). Dans ce cas, et si aucune valeur n'est passée par -v mode=, je prend la valeur par défaut mode = eco.

J'ai aussi placé un petit message d'erreur qui s'affiche si aucune valeur de mode valide n'est trouvée.

J'effectue le test de comparaison des modes avec une constante de chaine (string constant), c'est plus lent mais le modèle avec expression régulière (regex) n'a pas voulu se plier à ma volonté. Ce point reste donc à améliorer.


Voilà pour le compte rendu du jour.


   [ Message modifié par  La_Brosse  le  14-10-2011  à  18:14 ] 
Domos - Apprendre le langage AWK