Bash scripting Course
Lesson 1: The Foundation
- What you need:
- an ascii editor. Vim on linux, emacs, whatever.
- a linux/unix machine with bash installed.
- Vocabulary: in programming, you need to understand the 5 following words:
- constant: things that don't change
- variable: things that do change
- type: sort of
- procedure: piece of code
- function: piece of code returning a result
- scope: visibility
Many languages don't have a procedure anymore - you just use a function that doesn't return anything.
Similarly constants are just variable that don't change (ideally). (*)
JS is amongst those: it doesn't know procedure or constants. It is still usefull to use them.
It is a good idea to mark the type of a variable in the name.
For example: screenB is of type boolean, screenS is a string, screenI is an integer.
Writing convention are useful to ensure you spell things consistently. Since unix is case-sensitive, it is more than important.
(*) Hence the famous programmer joke: constants aren't - variables won't.
[Top]
Lesson 2: The basics
I have taken as first example a simple script that I wrote to automatically download TV programs.
#! /bin/bash
XMLTVDir="/home/francois/.xmltv"
echo "Generation master XML for TV programs"
today=`date +"%Y%m%d"`
cd $XMLTVDir
mkdir $today
cd $today
echo "Generating UK senders"
tv_grab_uk_bleb > uk.xml
echo "Generating French senders (this can take up to 4 hours!)"
tv_grab_fr > fr.xml
echo "Done - You can now use GenProg for fresh html listings"
cp uk.xml ../latest
cp fr.xml ../latest
Examinons cela ligne par ligne.
La première ligne est la plus importante:
#! /bin/bash
C'est elle qui va déterminer l'interpréteur de commande que le système va devoir charger pour comprendre la suite des instructions.
Le shell le plus utilisé sous linux est bash, mais ce n'est pas une évidence ! Chez les vieux programmeur unix, le shell par défaut est en général tcshell, et sur Solaris, c'est korn. C'est important de le savoir car la syntaxe n'est pas la même (voir: BASIC)
XMLTVDir="/home/francois/.xmltv"
ça, c'est une variable. On assigne à la variable XMLTVDir la valeur "/home/francois/.xmltv". C'est une chaîne de caractère, donc elle est entre guillemets.
C'est une variable, mais en fait elle va nous servir de constante, car sa valeur ne changera pas pendant toute la durée du programme. Je travaille souvent avec des constante et des variable car c'est plus facile par la suite de modifier le programme si je veux le changer de machine ou simplement si je ré-organise mes répertoires.
Par définition, quand il ne se passe rien, l'utilisateur devient nerveux, moi compris. Donc je parsème mes script de retour à l'écran. En plus cela facilite le débogage car on voit tout de suite si le programme est arrivé à une ligne ou pas.
Donc on envoie de l'info à l'écran:
echo "Generation master XML for TV programs"
(aka: à quoi sert ce truc)
today=`date +"%Y%m%d"`
Une autre assignation de variable, mais beaucoup plus intéressante.
Décomposons les choses:
today=``
C'est ce qui donne à unix toute sa puissance. La valeur que l'on va donner à la variable est le résultat d'une commande système, que l'on appelle en la mettant entre simple guillemets (pas le double de la déclaration de chaîne), tournés vers la droite.
La plupart des gens l'ignore, mais nous avons sur le claviers 3 guillemets simples:
- Le vertical: ' qui est le plus utilisé, c'est en général l'apostrophe. Sur l'azerty il est sur la première rangée, en dessous du 4.
- Celui tourné vers la gauche:´, troisième ligne du clavier, avec le ù et le %, accès par AltGr
- Celui tourné vers la gauche:`, troisième ligne du clavier, avec le µ et le £, accès par AltGr
Date renvoie par défaut un truc comme cela: Tue Feb 27 15:35:08 CDT 2015
Mais on peut passer des paramètre pour avoir le résultat que l'on veut. Voir à cet égard le manuel unix, c'est un cours de bash scripting, pas de unix :-)
Bref. Ce dont j'ai besoin, c'est la date sous la forme aaaammjj, donc: 20150215. Le paramètre pour cela est %Y%m%d.
Donc date +"%Y%m%d
today=`date +"%Y%m%d"`va donc attribuer à la variable today la valeur 20150211
cd $XMLTVDir
: Comme je le disais, on peut utiliser dans un script n'importe quelle commande unix, ici en l'occurrence cd qui permet de changer de répertoire. On lui passe comme paramètre le répertoire où on veut aller, en l'occurrence ce qui est contenu ($) dans la variable XMLTVDir.
Il est à noter que le script ne vérifie pas si le répertoire existe.
Une fois dans ce répertoire, on crée une sous répertoire qui va avoir pour nom la date du jour, contenue dans ($) today:
mkdir $today
Ici non plus, on ne vérifie pas qu'il n'y a pas d'erreur (pas bien !)
On va dans le répertoire en question et on affiche un truc :
cd $today
echo "Generating UK senders"
Puis on appelle un programme installé qui s'appelle tv_grab_uk_bleb et on redirige l'output ver un fichier uk.xml situé dans le répertoire courant.
tv_grab_uk_bleb > uk.xml
Une fois que c'est fait, on prévient que la suite va prendre du temps et on fait la même chose pour les émetteurs français:
echo "Generating French senders (this can take up to 4 hours!)"
tv_grab_fr > fr.xml
Enfin on informe l'utilisateur que c'est fini et qu'il peut passer à la suite (on pourrait lancer le programme suivant depuis le script, mais la procédure est volontairement coupée en morceaux):
echo "Done - You can now use GenProg for fresh html listings"
et on copie les fichiers généré dans un autre répertoire, qui contient toujours la dernière version.
cp uk.xml ../latest
cp fr.xml ../latest
Cela permet de garder un historique au cas où les procédures suivantes détruiraient les fichiers – on ne doit pas repasser 4h à les descendre.
De plus, les scripts suivants pointeront toujours sur "latest", sans se préoccuper de la date.
Mis à part le truc de la date, jusqu'à présent, c'est pas bien compliqué ? :-)
[Top]
Lesson 3: Really Starting
On continue avec le deuxième script, celui qui va transformer les XML obtenus sur le net en quelque chose d'intéressant. La plus grosse partie de cette transformation est faite par une humm heu – transformation XSL, mais le script a quelques éléments intéressants.
#!/bin/bash # where are the listing ? XMLTVDir="/home/francois/.xmltv/" # some functions we'll need Sort() { echo "Sorting $1.xml" sax $1.xml $XMLTVDir/util/cl.xsl > $1s.xml } Genere() { echo "Generating $1.htm" sax $1s.xml $XMLTVDir/util/prgs.xsl > $1.htm } #Rock'n roll echo "Generation HTML for TV programs" cd $XMLTVDir/latest if [[ $# -eq 0 ]] ; then # to move to external config Sort uk Sort fr # Genere uk # Genere fr exit 0 else for var in "$@" do Sort $var # Genere $var done fi # have a look #xombrero uk.htm fr.htm &Reprenons ligne par ligne.
#!/bin/bash # where are the listing ? XMLTVDir="/home/francois/.xmltv/"Same procedure as last year: on déclare notre interpréteur et une variable (qui sera une constante) – rien de bien neuf ici.
# some functions we'll need Sort() { echo "Sorting $1.xml" sax $1.xml $XMLTVDir/util/cl.xsl > $1s.xml } Genere() { echo "Generating $1.htm" sax $1s.xml $XMLTVDir/util/prgs.xsl > $1.htm }Ici c'est beaucoup plus rigolo et plus utile. Comme subtilement indiqué en commentaire, je déclare/crée des fonctions. Des fonctions c'est quoi ? tout simplement des bouts de code que je risque d'être amené à utiliser plusieures fois. Alors, au lieu de le ré-écrire, je l'isole dans une fonction spéciale que je n'écris qu'une fois et que j'appellerai chaque fois que j'en aurai besoin. Pour faire une fonction, on donne un nom, on met des parenthèses, et on met le code entre accolades. Simple.
MaFonction() { #des trucs ici – mon code }En l'occurrence j'ai deux fonctions, Sort et Genere qui appellent saxon (le processeur XSL) en lui passant les bon paramètres. Pour être utile, une fonction doit pouvoir être utilisée dans des circonstances différentes, des circonstances variables. Je vais donc passer en paramètre à ma fonction le nom du fichier à traiter. Ce sera le premier paramètre: $1 Il est à noter que le script ne fait encore rien. La description des fonctions n'entraîne pas leur exécutions tant qu'elle ne sont pas appellées dans le code.
#Rock'n rollLe vrai programme commence ici.
echo "Generation HTML for TV programs" cd $XMLTVDir/latestComme d'habitude, un message pour signaler ce qui se passe, et on va se positionner au bon endroit.
if [[ $# -eq 0 ]] ; then # to move to external config Sort uk Sort fr # Genere uk # Genere fr exit 0 else for var in "$@" do Sort $var # Genere $var done fiBeaucoup de choses dans ce petit bout de code, 7 pour être précis. Regardons pas à pas.
if [[ ]] ; then else fi (le fi marque la fin de notre boucle de condition)tout d'abords une des boucles les plus utilisée en programmation: si / alors / sinon. (1) Cela permet de tester une condition et de réagir en conséquence. La condition se trouve entre double crochets ([[ ici ]]). La condition c'est quoi ? si le nombre de paramètres du script ($#) (2) est null (equal zéro –eq 0). (3) Donc:
if [[ $# -eq 0 ]] ; thenSi le script est appellé sans paramètre
Sort uk Sort frOn appelle la procédure de tri avec uk et fr en paramètre, c-a-d On trie uk.xml et on trie fr.xml en envoyant le résultat dans uks.xml et frs.xml respectivement
# Genere uk # Genere frPuis on appelle la procédure de génération avec lesdits paramètres (en fait en commentaire dontc plus maintenant mais bon)
exit 0et puis on sort en disant que tout va bien (on sort avec un code d'erreur=0) (4)
elsesinon (c-a-d si on a des paramètres) on entame l'autre boucle la plus utilisée, pour autant que
for var in "$@"pour chaque (autant que de) variable (var) (5) se trouvant dans la liste des paramètre passée ($@) (6)
dofaire (début de ma boucle) (7)
Sort $varAppeler la procédure en lui passant comme paramètre le contenu de la variable en question ($var).
# Genere $varidem
Donefini (fin de ma boucle)
fi(fin de l'autre branche de ma condition)
# have a look #xombrero uk.htm fr.htm &(Lancer un browser pour visualiser les pages qui résultent du script.) Voilà
- Les deux boucles les plus utilisées en programmation,
- la création et l'utilisation d'une procédure,
- une condition de test,
- l'utilisation des paramètres passés au script ou dans le script,
- le nombre de paramètre du script et leur récupération
- les codes de sortie
Pas mal de concepts pour un bête petit script de 30 lignes. La bonne nouvelle c'est que pratiquement tout est là.
[Top]