Objectifs :
L’objectif central de ce TP va être de créer une fonction pour afficher notre arborescence de fichier par récursion en bash.
Une fonction récursive est une fonction qui s’appelle elle même pour opérer son traitement, on parle alors d’appels récursifs de fonction.
Dans la première partie, on fera un exercice introductif à la récursion avant de se lancer dans la création du script bash pour faire notre arbre. On croisera les flux sur notre route.
Un second objectif va être de comprendre les notions de sections critiques et de mutex et de réussir à les mettre en pratique.
Nous allons créer une fonction arroser
permettant d’incrémenter une valeur dans un fichier plante.
Q1 : Dans un fichier arrosoir.sh
, créez une fonction arroser()
. Pour le moment, cette fonction doit afficher
“Je m’amuse tellement à faire du bash” dans le terminal (ne pas tricher en omettant le ’ dans la chaîne).
Q2 : Exécutez cette fonction dans le script.
Q3 : Créer un fichier nommé plante
grâce à une commande. Ce fichier contient uniquement le chiffre 0
sur sa première ligne.
Q4 : Modifiez la fonction arroser
afin qu’elle affiche la première ligne du fichier plante
dans le terminal.
Q5 : Modifiez la fonction arroser
pour que l’appel du script prenne un argument x en paramètre et écrive
la valeur de ce paramètre à la place du 0 du fichier plante
.
Q6 : Remettez 0 dans le fichier plante
. Créez une fonction boucleImp
qui va lire puis incrémenter autant
de fois la valeur contenue dans le fichier plante
que la valeur de x via une boucle for ou while. La fonction arroser
appelle maintenant la fonction boucleImp
.
Normalement, à ce stade, vous pouvez faire ./arrosoir.sh 15
pour voir quinze fois la valeur de plante
être
augmentée de un.
Q7 : Créez une fonction boucleRec
qui suit le comportement suivant (et où x est le paramètre donné lors
de l’exécution de la fonction) :
Si X n’est pas nul :
- On enlève 1 à X
- On appelle la fonction boucleRec
Sinon :
- On affiche "X n’est plus"
Q8 : Modifiez boucleRec
pour faire en sorte qu’elle incrémente en plus la premier ligne du fichier plante
.
Le script arrosoir.sh
utilise maintenant boucleRec
au lieu de boucleImp
.
Normalement, vous avez à ce stade un script au comportement analogue à celui de Q6 mais avec un fonctionnement récursif (i.e qui s’appelle soit même pour mener à terme son traitement).
L’objectif de cet exercice est de créer un script permettant d’afficher l’arborescence de fichiers à partir
du répertoire courant. Toute explication demandée doit être rendue dans un fichier texte explication.txt
.
Q9 : Créez l’arborescence de fichier suivante (Rx sont des répertoires et Fx sont des fichiers) avec le minimum de commandes possibles :
Explicitez ce que vous avez tapé dans le terminal dans explication.txt
.
Q10 : Créez un script treeRec
permettant de savoir si l’élément pris en paramètre est un fichier ou un
répertoire puis affiche son nom. Il faut que l’exécution du script se termine en renvoyant un statut de succès.
Expliquez comment retourner un statut d’échec dans explications.txt
.
NT : "-d chemin"
dans un if permet de déterminer si le chemin pointe vers un répertoire.
Q11 : Élargissez votre script précédent pour qu’il traite l’ensemble des éléments du répertoire courant au lieu d’un seul élément.
Pour cela, on utilise une fonction bash qui permet de lister les éléments d’un répertoire dont on stocke le résultat dans une variable. Il faut ensuite parcourir la liste contenue dans cette variable à l’aide d’une boucle pour afficher le résultat sous la forme suivante :
-- Fichier foo.js
-- Fichier bar.php
-- Répertoire Barfoo
-- Fichier foubar.tlp
...
Q12 : Faites une fonction parseRec
qui contient le code déjà effectué dans la question précédente. Révisez
treeRec
afin qu’il utilise la fonction parseRec
.
Q13 : Révisez la fonction parseRec
afin que lorsque l’élément traité est un répertoire, un appel récursif
est effectué à l’intérieur de ce répertoire. Pour ce faire, le script rentre dans le répertoire, appelle la fonction parseRec
, puis ressort du répertoire. On affiche ainsi la liste complète des fichiers et des répertories de l’arborescence. En exécutant votre script dans le répertoire R1 de l’arborescence créée précédemment, vous
devriez avoir le résultat suivant :
---- R2
---- F1
---- F2
---- F3
---- R3
---- R4
---- F4
---- F5
À ce stade, il n’est plus nécessaire d’afficher si l’élément traité est un répertoire ou un fichier, vous pouvez supprimer cette information si vous le souhaitez.
Q14 : Créez une fonction deepness
qui prend un paramètre x et qui affiche la chaîne “----
” x fois.
NT : La commande echo
fait automatiquement un retour à la ligne. Vous pouvez utiliser la commande printf
qui permet d’éviter ce problème si nécessaire. Pour forcer un retour à la ligne avec printf
, ajoutez \n
à la fin de la chaîne.
Q15 : Mettre à jour l’affichage pour indiquer la profondeur de l’élément observé dans l’arborescence de
fichier. Pour ce faire, on utilise la fonction deepness
dans la fonction parseRec
. Vous devriez avoir le résultat
suivant :
---- R2
---- ---- F1
---- ---- F2
---- F3
---- ---- R3
---- ---- ---- R4
---- ---- ---- ---- F4
---- ---- ---- ---- F5
Q16 : Modifiez votre script afin qu’il puisse s’exécuter ailleurs que dans le répertoire courant. Si le script a un chemin en paramètre, il s’exécute à cet emplacement. Sinon, il s’exécute dans le répertoire courant.
Q17 : En supposant que le script fonctionne, si celui-ci s’arrête abruptement (ex : ctrl-c, bug, etc…), où serez vous positionné dans l’arborescence? Pourquoi? Expliquez dans le fichier explications.txt
.
Soit le script ecriture.sh
suivant :
#! /bin/bash
if [ $# -lt 1 ] ; then
echo "Il faut au moins un parametre"
exit 1
fi
for elem in "$@" ; do
if [ ! -e "$elem" ] ; then
echo premier $$ > "$elem"
else
echo suivant $$ >> "$elem"
fi
done
Q1 : Exécutez deux fois de suite la commande ./ecriture.sh a b c
et expliquez le contenu des fichiers.
Q2 : Soit le script lancement_ecriture.sh
suivant qui permet d’exécuter en concurrence deux processus ecriture.sh
.
#! /bin/bash
rm -f f1 f2 f3
./ecriture.sh f1 f2 f3 & ./ecriture.sh f1 f2 f3
wait
Exécutez ce script jusqu’à ce que le contenu d’un des fichiers f1
, f2
ou f3
ne contienne qu’une seule ligne au lieu de deux, c’est-à-dire, jusqu’à ce qu’une des écritures soit perdue. Expliquez le résultat obtenu.
Q3 : Identifiez la section critique dans ecriture.sh
.
Q4 : Modifiez le script ecriture.sh
de façon à assurer une exclusion mutuelle sur la section critique.
Q5 : Reprenez le code de la question précédente, mais en faisant attention à ne pas bloquer les processus s’ils n’écrivent pas dans le même fichier.
arrosoir.sh
#! /bin/bash
function boucleRec() {
x=$1
if [ "$x" -ne 0 ]; then
# increment value in file
line=$(head -n 1 plante)
line=$((line+1))
echo $line > plante
# call recursively
x=$((x-1))
boucleRec $x
else
echo "X n’est plus"
fi
}
function boucleImp() {
for i in `seq 1 $1`;
do
line=$(head -n 1 plante)
line=$((line+1))
echo $line > plante
done
}
function arroser() {
boucleImp $1
}
#arroser $1
boucleRec $1
treeRec.sh
#! /bin/bash
function deepness {
for i in `seq 1 $1`;
do
printf "%s " "----"
done
printf "\n"
}
function parseRec {
d=$(deepness $2)
if [ -d $1 ]; then
# do not print root folder
if [ $2 -gt 0 ]; then
echo $d $1
fi
# recurse for children
s=$(ls $1)
cd $1
for f in $s; do
parseRec $f $(($2+1))
done
cd ..
elif [ -f $1 ]; then
echo $d $1
else
echo $1 is invalid
exit 1
fi
}
p=$1
if [ -z $p ]; then
p="."
fi
parseRec $p 0
exit 0
lancement_ecriture.sh
#! /bin/bash
rm -f f1 f2 f3
./ecriture.sh f1 f2 f3 & ./ecriture.sh f1 f2 f3
wait
ecriture.sh
#! /bin/bash
if [ $# -lt 1 ] ; then
echo "Il faut au moins un parametre"
exit 1
fi
for elem in "$@" ; do
# lock
./P.sh "$elem".lock
if [ ! -e "$elem" ] ; then
echo premier $$ > "$elem"
else
echo suivant $$ >> "$elem"
fi
# un-lock
./V.sh "$elem".lock
done