Programmation Tcl
Et c'est reparti pour de la prog !! Ouais !!
Tcl est un langage de programmation dont vous
avez sûrement déjà entendu parler. Mais pas tout seul
: on entend parler plus souvent de Tcl/Tk ou de Tcl/Expect etc.
Il faut l'avouer Tcl est loin d'être un langage évolué,
il peut être très intéressant pour débuter en programmation
mais ses capacités sont très limitées. Tcl est la langage
utilisé pour coder les eggdrops (j'entre pas plus dans ce sujet que
je ne maîtrise pas).
Cet article est une suite de trois articles : maintenant nous allons étudier
le langage Tcl, ensuite nous utiliserons Expect qui peut être vu comme
une "extension" de Tcl et pour finir, l'objectif final : nous essayerons
de coder un vers avec ces langages.
Pourquoi choisir Tcl pour faire un worm plutôt que le C ? Premièrement
parce que l'objectif était avant tout de faire un article pas forcément
technique sur les worms. Il s'agissait plutôt de comprendre la façon
dont ils fonctionnent, les différentes étapes etc. Ensuite c'est
mon premier worm donc faut pas s'attendre à une version en C#31337
(c'est une nouvelle version du C LoL).
C parti : les variables
Je le répète : vous n'avez pas besoin d'avoir déjà
programmé pour faire du Tcl. C'est un langage très intuitif
(les instructions sont parlantes). A part ça Tcl est surtout un langage
interprété, tout comme le Perl, le Bash, le PHP. Bref pas de
compilation comme en C, C++... On appelle un programme en Tcl un script.
Pour faire du Tcl vous avez besoin d'un interpréteur Tcl. Celui-ci
se nomme Tclsh. Il peut être utilisé comme un shell, ce qui explique
le 'sh' à la fin de Tclsh.
Tcl est à priori dispo pour Windows, il suffit de le télécharger.
Pour les utilisateurs de Linux, il est déjà installé
(faites un 'whereis tclsh' pour savoir ou se trouve l'interpréteur).
Tcl signifie Tool Command Language. C'est donc un programme qui ne fait qu'appeler
des commandes, tout comme le fait un programme bash ou batch.
Tcl ne manipule que des chaînes de caractères. Bien sûr
on peut générer des nombres, les additionner... mais Tcl les
conserve en mémoire comme étant des chaînes de caractères.
La syntaxe du langage est très simple
;-) :
commande arg arg arg ...
Bref c comme si vous tappiez une commande sous Unix.
Il n'est pas nécessaire de déclarer les variables en Tcl (comme
pour le PHP). Les variables commencent par le caractère dollar ($).
Lorsque l'on fait référence à une variable, Tcl la remplace
par sa valeur et exécute la commande demandée. L'affectation
d'une valeur à une variable ne demande pas la présence du $.
Exemple :
set nom
"Alice"
puts "Mon nom est $nom"
Donnera l'affichage : Mon
nom est Alice
A la première ligne, la variable 'nom' est crée et la valeur
'Alice' lui est attribuée. A la seconde ligne, Tcl remplace '$nom'
par 'Alice' (substitution) et lance la commande
puts qui permet l'affichage de texte à l'écran.
Deuxième exemple :
set jour 23
set mois 11
set annee 2003
set date "$jour:$mois:$annee"
puts $date
Provoque l'affichage : 23:11:2003
Les commandes
Maintenant que nous avons vu la substitution des variables, pourquoi ne pas
étudier la substitution des commandes. Cette méthode, très
utilisée en Tcl, fonctionne de la même façon que pour
les variables : Tcl commence par faire les calculs demandés par la
ou les commandes. Le résultat est une chaîne de caractère
qui vient remplacer les commandes qui l'ont générées.
Tcl effectue ensuite la commande principale. Cela passera mieux avec un exemple
;-)
set ope "puts
HelloWorld"
eval $ope
On obtient : HelloWorld.
La commande eval permet comme son nom l'indique d'évaluer une commande.
Ici on déclare une variable 'ope' qui contient la commande 'puts
HelloWorld'.
La deuxième étape est l'évaluation de cette commande.
Tcl voit qu'il doit évaluer la variable 'ope'. Comme c'est une variable,
il la substitue par sa valeur : 'puts HelloWorld'
puis il lance la commande obtenue.
Une autre façon de faire des substitutions de commandes est d'utiliser
des crochets. Dans l'exemple suivant nous appelons la commande expr qui permet
d'effectuer des opérations mathématiques.
set euros 15
set francs [expr $euros*6.55957]
puts $francs
donne 98.39355.
Les crochets remplacent donc une commande par son résultat.
Quelques précisions à apporter sur la syntaxe de Tcl : tous
les arguments doivent être des chaînes de caractères. Les
arguments doivent être séparés par un espace ou une tabulation.
Les commandes sont soit séparées par un point virgule ou un
retour à la ligne.
Les caractères spéciaux
Il suffit de mettre un backslash devant un caractère pour lui enlever
ou lui rajouter un sens spécial.
set phrase "J'ai
20\$ dans\nmon portefeuille"
puts $phrase
donnera :
J'ai 20$ dans
mon portefeuille
Vous trouverez les caractères spéciaux dans cette annexe.
La commande expr permet d'effectuer beaucoup
d'expressions arithmétiques.
expr 1 == 0 donnera
0 (opération booléenne) expr
5+4 renverra 9.
Les opérateurs arithmétiques et fonctions mathématiques
disponibles sont listées ici.
Une méthode utilisée pour empécher la substitution des caractères spéciaux et donc des variables etc. est l'utilisation des accolades. Les accolades empèchent la substitution. Tout ce qui se trouve entre accolades est pris tel quel, même les passages à la lignes. Reprenons la phrase suivante :
set phrase {J'ai
20$ dans
mon portefeuille}
donne le même résultat que tout à l'heure.
Les commentaires
Ils commencent par le cacarctère dièse (#). Si ils se trouvent
après une instruction cette instruction doit se terminer par un point-virgule.
#Ce script affiche la longueur de la chaine "Hello"
additionné à la valeur 7
set len [expr [string length Hello]
+ 7] ; #deux évaluations de commande
puts $len ;# Affiche 12
Ne vous inquietez pas pour les commandes, elles sont toutes dans l'annexe.
D'autres commandes
unset permet la destruction d'une variable : unset
phrase (on ne met pas le dollar)
append permet de concaténer des variables
set phrase "LOTFREE"
append phrase " n°6"
puts $phrase
donne LOTFREE n°6
La commande format permet de choisir un format
d'affichage de la même façon que les commandes printf en C
set a 24.236784; set b secondes
puts [format "a = %5.3f %s"
$a $b]
donne a = 24.237 secondes
Il existe bien d'autres commandes ce serait trop long de toutes les passer en revue.
Les listes
Les listes constituent une des capacités intéressantes de Tcl.
Les commandes dédiées aux listes commencent généralement
par la lettre 'l'.
Une liste n'est rien de plus qu'un ensemble de chaînes de caractères
séparées par des blancs.
set a 5;# a=5
set liste1 {un deux trois}
;# liste1="un deux trois"
set liste2 "4 $a 6"
;# liste2="4 5 6"
set liste3 [list $liste1 $liste2]
puts liste3 ;# Donne
{un deux trois} {4 5 6}
Les listes permettent d'ajouter des éléments
de façons dynamique et d'y accèder simplement etc.
Malheureusement ça permet pas de tuer ce con de Pikachu !!
Les tableaux
set montableau(0) "Zéro"
set montableau(1) "Un"
set montableau(2) "Deux"
for {set i 0}{i < 3}{incr i 1}{
puts $montableau($i)
}
donne :
Zéro
Un
Deux
Comme pour le PHP un tableau peut avoir des indices non numériques : set montableau(animal) "Kangourou" ;-)
Les variables d'environnement sont accessibles à travers le tableau prédéfinis 'env'. Ex : puts $env(DISPLAY)
Les tests
Comme dans tout langage de programmation il est possible de faire des branchements
conditionnels.
Les structures possibles sont les suivantes :
if {test} {
instructions_à_effectuer_si_la_condition_est_vérifiée
}
Il est possible d'exécuter des instructions
si la condition n'est pas vrai :
if {test}{
instructions_à_effectuer_si_la_condition_est_vérifiée
} else {
instructions_à_effectuer_si_la_condition_n'est_pas_vérifiée
}
Enfin avec plusieurs tests à la suite
:
if {test_1}{
instructions_à_réaliser_si_la_condition_test_1_est_vrai
} elseif {test_2}{
instructions_à_réaliser_si_la_condition_test_1_n'est_pas_réalisé_mais_que_la_condition_test_2_est_bonne
} else {
instructions_à_effectuer_si_aucune_des_conditions_ne_s'est_réalisée
}
Attention !! On ne peut insérer de sauts de ligne qu'à l'intérieur des accolades alors utilisez-les toujours de la façon que l'on vient de voir.
Pour ce qui est des conditions, ce doit être
des variables dites 'booléennes' (qui prennent comme valeur, soit vrai,
soit faux).
Pour obtenir une valeur booléenne on peut par exemple comparer deux
chiffres.
Inférieur : $a < $b
Supérieur : $a > $b
Egal : $a == $b
Différent : $a != $b
Supérieur ou égal : $a >= $b
Inférieur ou égal : $a <= $b
Vous n'êtes pas obligé de comparer
des variables (ouf !!), vous pouvez comparer une variable avec une valeur
ou deux variables ensembles etc.
if {$nom == "sirius"
} {
...
} elseif { $val < 5}{ ...
Les boucles
La façon dont les boucles sont formées n'est
pas si éloignées des structures conditionnelles. Commençont
par la boucle for :
for {initialisations} {test_de_fin_de_boucle}
{incrémentations}{
corps_de_la_boucle
}
Exemple :
for {set i 0} {$i < 6} {incr
i}{
puts -nonewline $i
}
donnera comme résultat : 012345
L'option nonewline permet simplement de ne pas passer à la ligne à
chaque affichage.
La boucle while, permet de répéter
une instruction (ou une suite d'instructions) tant que la condition générale
est vérifiée.
set i 10
while {$i > 0}{
puts $i
incr i -1
}
feras passer la variable $i de 10 à 0.
Une commande dérivée de la boucle
for est la boucle foreach qui permet de traiter tous les élèments
d'une liste.
foreach variable {liste_de_variables_à_prendre}{
corps_de_boucle
}
exemple :
puts "Les distribs
Linux sont :"
foreach nom {Mandrake RedHat Debian Knoppix Slackware
Suse Caldera}{
puts $nom
}
Affichera les différentes distrib Linux. Cette commande est très
intéressante avec les variables de type liste.
Comme on a pu le constater, à chaque passage dans la boucle la variable
après le mot clé foreach prend pour valeur l'élément
suivant de la liste. L'affectation peut se faire avec deux valeurs :
foreach {style artiste} "Punk
Rancid Ska Ska-p Rap Svinkels" {
puts "$artiste joue du
$style"
}
Les branchements à choix multiples
Ils permettent de comparer une variable à un grand nombre de valeurs.
La syntaxe est : switch chaîne valeur_1 bloc_1 valeur_2 bloc_2 ... valeur_n
bloc_n default bloc_default
Un bloc est le mot pour désigner une suite d'instructions. Voici un
exemple type :
switch $val {
0 {puts "Zéro"}
1 {puts "Un"}
2 {puts "Deux"}
default {puts "Désolé
je ne sais pas traduire ce nombre"}
}
La principale différence avec les autres langages de programmation
c'est qu'il n'y a pas besoin de mettre un "break" à la fin
de chaque bloc.
Toutefois les commandes 'break' et 'continue' existent. 'break' permet de
quitter une boucle for, while ou foreach et 'continue' permet de passer au
prochain tour de boucle sans terminer la boucle en cours.
Les procédures
Pour ce qui connaissent pas le mot 'procédure', et bien
en fait ce sont des fonctions qui renvoient un résultat. Du moins dans
le langage Tcl c'est comme ça puisque les fonctions n'existent pas
chez lui.
proc nom_de_la_procédure {arguments}{
corps_de_la_procédure
}
Bien que les arguments soient entre accolades, il ne s'agit pas forcément
de listes. Cela peut être tout type de variable. Voici une procédure
qui prend un nombre en arguments et renvoie sa valeur au carré :
proc carre {valeur}{
set resultat [expr pow($valeur,2)]
return $resultat
}
Pour appeler cette fonction on peut par exemple faire :
set x 2
puts [carre $x];#La substitution de commandes,
vous vous rappelez j'espère ;-)
et on obtient : 4.0
On aurait obtenu le même résultat
si on n'avait pas mis la commande return. Pourquoi ? Parce que en l'absence
de valeur de retour, Tcl renvoie le résultat de la dernière
commande.
Une procédure qui calcule une moyenne à partir des chiffres
d'une liste :
proc moyenne {liste}{
set longueur [llength $liste]; #On
récupère le nombre de chiffres dans la liste
if {$longueur > 0}{
set somme 0
for {set i 0} {$i <=
[expr $longueur-1]} {incr i}{
incr somme [lindex
$liste $i]; #On fait la somme de tous les chiffres de
la liste
}
append longueur .0 ;#Un
'truc' pour obtenir un réel au final
return [expr $somme/$longueur]
;#Moyenne = somme_des_chiffres / nombre_de_chiffres
}
}
Bon là on a fait une bonne partie. Passons
aux Flux.
Déjà on connait la commande d'écriture sur un flux :
puts
La commande de lecture est gets
Ok on sait lire et écrire mais avant tout il faut ouvrir le flux, pour
cela on utilise la commande open. Quand
on a fini les traitements on appelle la commande close
pour fermer ce flux. Voici un exemple pour illustrer tout cela :
set f [open "l33t.txt"
"w"]
puts $f "It\'s a l33t text"
puts $f "Enjoy it"
close $f
Commençons par la commande 'open'
: le premier argument est le fichier à ouvrir et le second est le mode
d'ouverture. Voici les différents modes d'ouverture d'un fichier :
* r : ouvre le fichier en lecture seulement, le fichier doit déjà
exister.
* r+ : ouvre le fichier en lecture et écriture, le fichier doit déjà
exister.
* w : ouvre le fichier en écriture seulement, il le remet à
0 si il existe déjà sinon il crée un nouveau fichier
* w+ : ouvre le fichier en lecture et écriture, il le remet à
0 si il existe déjà sinon il crée un nouveau fichier
* a : ouvre le fichier en écriture seulement, crée un nouveau
fichier vide s'il n'existe pas, se positionne à la fin du fichier.
* a+ : ouvre le fichier en lecture et écriture, crée un nouveau
fichier vide s'il n'existe pas, se positionne à la fin du fichier.
La commande open
renvoie un 'canal', c'est lui qui représente le flux sur lequel on
va écrire. Bref c'est un 'file descriptor'. La suite du programme est
simple : on écrit deux lignes et on ferme le fichier.
Un exemple de lecture dans un fichier :
set f [open "l33t.txt"
"r"]
set ligne1 [gets $f]
set long_ligne2 [gets $f ligne2]
close $f
puts "ligne
1 : $ligne1"
puts "ligne 2 : $ligne2"
puts "Longueur de la ligne 2 : $long_ligne2"
On obtient :
ligne 1 : It's a l33t text
ligne 2 : Enjoy it
Longueur de la ligne 2 : 8
Et une lecture au clavier :
puts -nonewline "Entrez
votre nom :"
gets sdtin nom
L'utilisation des sockets est presque similaire.
Il suffit d'utiliser la commande socket à la place de open.
Utilisez les nouvelles sections des manpages pour avoir plus d'infos sur les
commandes Tcl. Par exemple faites un "man n socket" sous Linux.
Bon on va pouvoir passer à expect maintenant :-)