But de ce tutorial
Au-dessus des infoducs sur les filtres on peut considérer les scripts, qui permettent de structurer des éléments eux-mêmes complexes. On touche ici à quelque chose se rapprochant plus de la programmation classique.
if : then / else ; case in ; while do ; for in... Avec quelques scripts classiques : calcul d'une factorielle, calculette, filtrer les fichiers.
a) Un simple script de test >> vers le
sommaire
Il existe plusieur shell pour interpréter un script : Bash, ksh, zsh...
La première ligne peut donc spécifier le type de shell dont
on a besoin. A l'heure actuelle, zsh est le plus complet. Donc :
#!/bin/zsh
Cette ligne spécifiera le shell qui doit exécuter le script.
Maintenant, on veut faire un simple test en affichant quelques uns des arguments
parmis ceux qui donnent l'utilisateur. Ainsi :
#!/bin/zsh
if
[[ $# -eq 0 ]]
then
exit
1
fi
# Ceci est un commentaire, bien précédé de #. Un
test "si" a la structure suivante :
#
if
[[ condition ]]
#
then
action1
#
else
action2
#
fi
if
[[ $1 == "aller" ]]
then
if
[ $# -gt 1 ]
then
echo
Nous allons
dans le repertoire $2
cd
$2
else
echo
Specifiez
simplement le repertoire...
fi
else
echo
"Vous ne voulez aller dans aucun repertoire. Imprevu..."
echo
"Les parametres d'appel du script $0 sont $*"
fi
Si ce script n'a aucun argument, il va se fermer (condition $# -eq 0). Sinon,
on continue. Si le premier argument est "aller", alors on regarde
:
- si on a d'autres arguments, le deuxième est un nom de répertoire
où on se déplace
- s'il n'a pas plus d'un argument, on affiche un message demandant de spécifier
le répertoire
Enfin, si le premier argument n'est pas "aller" on affichera le
nom du script et ses paramètres d'appels.
On remarquera donc que $# donne le nombre de paramètres, $* la liste
complète, et $n permet de sélectionner le n-ième paramètre.
On a d'autres possibilités comme $$ qui donne l'identifiant du processus,
ou $? qui permet de connaître le statut de sortie de la dernière
commande (nous avons fait un exit 1 : le statut après cela est de 1).
Dans la condition, on remarquera :
- -eq pour l'égalité
- -lt pour inférieur (less than)
- -gt pour supérieur (greater than)
- -ne pour différent (not equal)
- = et != pour tester une égalité avec une chaîne de caractères
b) Utiliser des filtres >> vers le sommaire
On veut faire un script qui affiche le contenu d'un fichier situé entre
deux colonnes. Par exemple, l'utilisation de ce script sera couper.sh texte.txt
3 10 ; en résultat on affichera le contenu du fichier se trouvant entre
le 3ème et le 10ème caractère (colonne). D'où
:
#!/bin/zsh
if
[[ $# -ne 3 ]]
then
print
"Donnez
un nom de fichier et deux entiers."
elif
[[ -f $1 ]]
then
print
"Le
fichier existe, nous allons le traiter."
if
[[ $4 -gt $3 ]]
then
cut
-c $3-$4
$1
else
print
"$3
est plus grand que $4, incorrect"
fi
else
print
"Acces impossible a $1"
fi
fi
Si nous n'avons pas au moins 3 paramètres, on ne peut pas faire le script. Après, on vérifie que ce soit un fichier et qu'on demande des colonnes logiques pour passer les arguments au filtre cut.
En effet, le if possède d'autres possibilités :
- -d $n pour vérifier si $n est un répertoire
- -f $n pour vérifier si $n est un fichier
- -x $n pour vérifier si $n est un fichier exécutable
c) Développer l'interactivité et le test multiple
>> vers le sommaire
Le but est ici de créer un script de type calculette. On lui donne
un nombre, un opérateur puis un nombre et il fait l'opération.
Par exemple calcul.sh 2 + 3 doit renvoyer 5. D'où :
#!/bin/zsh
if
[[ $# -ne 3 ]]
then
print
"Il
faut 3 parametres.
Ni plus, ni moins..."
else
if
[[ $2 == "*"
]]
then
expr
$1 \* $3
fi
if
[[ $2 == "/"
]]
then
expr
$1 / $3
fi
if
[[ $2 == "+"
]]
then
expr
$1 + $3
fi
if
[[ $2 == "-"
]]
then
expr
$1 - $3
fi
fi
On fait ici appel à la commande expr
qui permet d'évaluer une expression
(consultez son manuel pour plus d'informations). Il est stupide lorsqu'on
sait par exemple que $2 est égal à "*" de regarder
s'il est ensuite égal à /, + ou -. Cependant, quelles possibilités
? Il faudrait soit faire des else
-if
en cascade, soit sortir
après le then
(avec un exit 0 par exemple).
On propose d'utiliser une autre structure qui permet d'écrire de façon
propre une série de tests sur une variable :
#!/bin/zsh
if
[[ $# -ne 3 ]]
then
exit
1
fi
# On s'assure d'avoir nos trois opérateurs
case
$2 in
+)
expr
$1 + $3;;
-)
expr
$1 - $3;;
/)
expr
$1 / $3;;
"*")
expr
$1 \* $3;;
*)
print
"Aucun operateur conforme selectionne";;
esac
Cette structure permet de tester l'égalité de la variable parmis plusieurs possibilités ; en C++ ou Java, on fait un switch. Si aucune des possibilités n'est vérifiée alors on peut disposer d'un cas par défaut, marqué *). Pour éviter dans notre situation que celui-ci remplace l'opérateur * de la multiplication, on spécifie bien avant que notre opérateur de multiplication est le caractère *.
On souhaite maintenant développer l'interactivité en saisissant
les données de l'utilisateur au fur et à mesure de l'opération,
c'est-à-dire ne plus passer d'arguments. Ainsi :
opre une série de tests sur une variable :
#!/bin/zsh
# On ne s'occupe plus des arguments
print
"Premier nombre ? "
read
NOMBRE1
print
"Second nombre ? "
read
NOMBRE2
print
"Operateur ? "
read
OPERATEUR
case
$OPERATEUR in
+) RESULT=$(
expr
$NOMBRE1 + $NOMBRE2);;
-) RESULT=$(
expr
$NOMBRE1 - $NOMBRE2);;
/) RESULT=$(
expr
$NOMBRE1 / $NOMBRE2);;
"*") RESULT=$(
expr
$NOMBRE1 \* $NOMBRE2);;
*)
print
"Operateur non conforme";;
esac
print
"$NOMBRE1 $OPERATEUR $CHIFFRE2 = $RESULT"
Avec read on lit une entrée au clavier que l'utilisateur va taper, et on la stocke dans une variable qu'on déclare du même coup.
d) Faire une boucle >> vers le sommaire
On aimerait parfois pouvoir répéter certains ennoncés
un nombre de fois donner, c'est-à-dire réaliser simplement une
boucle. Pour cela, on utilise la structure suivante :
while [[ condition ]]
do
action
done
Par exemple, pour compter de 0 à 10 :
NOMBRE=0
while [[ $NOMBRE -lt 11 ]]
print
"$NOMBRE"
NOMBRE=$(expr
$NOMBRE + 1)
done
On peut aussi faire script pour calculer une factorielle, tel que n! = n*(n-1)*(n-2)*...*1
:
#!/bin/zsh
if
[[ $# -ne 1 ]] || [[ $1 -lt 0 ]]
then print
"On ne peut calculer une factorielle qu'en ayant
un nombre positif"
else
RESULT=1
N=$1
while
[[ $N -gt 1]]
do
RESULT=$(
expr
$RESULT * N)
N=$(
expr
$N - 1)
done
print
"factoriel($1)=$RESULT"
fi
Sur le même principe, on peut faire clignoter la LED (diode) du clavier
un certain nombre de fois, avec un filtre cligno.sh qui reçoit en argument
le nombre de fois :
#!/bin/zsh
if
[[ $# -ne 1 ]]
then print
"Definissez le nombre de fois a clignoter"
else
CLIGNO=-1
while
[[ $CLIGNO -lt $0]]
do
xset
-led 3
CLIGNO=$(
expr
$CLIGNO + 1)
xset
led 3
done
fi
e) Traiter une liste d'éléments >>
vers le sommaire
Une structure nous permet de faire une action sur chaque élément
d'un ensemble jusqu'à avoir parcouru tout l'ensemble :
for élément
in ensemble
do
action
done
Par exemple, l'extrait de script suivant va parcourir la liste des paramètres
et les afficher un par un :
for
i in $* #rappel
: $* est la liste des paramètres fournis au script
do
print "$i"
done
L'autre usage simple est la liste de documents. Par exemple, on souhaite
afficher le nom de tous les fichiers exécutables du répertoire
donné en paramètre :
#!/bin/zsh
if
[[ $# -ne 1 ]];
then
exit
1
for
var in $(
ls
$1)
do
if
[[ -x $var ]]
then
print $var
fi
done
Il y a bien entendu toujours d'autres moyens que d'utiliser cette structure
avec la liste pour faire une opération équivalente. Mais les
autres possibilités sont en général moins rapides, quand
elles ne relèvent pas du bidouillage... Le script précédent,
en remplacant -x par -f permet d'afficher tous les fichiers. Faisons un script
qui fait de même sans utiliser cette structure, avec ce que nous savons
:
#!/bin/zsh
nombre=$(
ls
-als |
wc
-l)
#nombre représente le nombre d'éléments listés
par la commande ls -als
i=1
while
[[ $i -lt $nombre ]]
do
CHAR=$(
ls
-als |
head
--lines="$i" |
tail
-1 |
cut
-c 6-6)
if
[[ $CHAR !=
"d" ]];
then
ls
-1 |
head
--lines="$i"
|
tail
-1;
fi
i=$(
expr
$i + 1)
done
On affiche la liste avec les permissions de tous les éléments. Si un élément n'est pas un répertoire (pas de d), on considère qu'il est un fichier et on affiche alors sa ligne. En executant ceci, on sera frappé par l'extrême-lenteur face à la méthode rapide, et propre, présentée avec la structure précédente.