Compléments sur la commande test
Opérations relatives aux fichiers
Vérifieer si deux fichiers partagent le même inode (ce sont des liens physiques)
if [ "$f1" -ef "$f2" ]
Déterminer lequel des deux fichiers est le plus récent
FICHIER1 -nt FICHIER2
FICHIER1 est plus récent (date de modification) que FICHIER2
Déterminer lequel des deux fichiers est le plus ancien
FICHIER1 -ot FICHIER2
FICHIER1 est plus ancien que FICHIER2
Vérifier que le fichier existe et qu'il s'agit d'un périphérique bloc
-b FICHIER
Vérifier que le fichier existe et qu'il s'agit d'un périphérique caractère
-c FICHIER
Vérifier que le fichier existe et qu'il s'agit d'un répertoire
-d FICHIER
Vérifier que le fichier existe
-e FICHIER
Vérifier que le fichier existe et qu'il s'agit d'un fichier ordinaire
-f FICHIER
Vérifier que le fichier existe et qu'il s'agit d'un lien symbolique
-h FICHIER / -L FICHIER
Vérifier que le fichier existe et qu'il s'agit d'un tube nommé
-p FICHIER
Vérifier que le fichier existe et qu'il s'agit d'une socket
-S FICHIER
Vérifier que le fichier existe et qu'il n'est pas vide
-s FICHIER
Vérifier que le fichier existe et qu'il dispose des droits de lecture
-r FICHIER
Vérifier que le fichier existe et qu'il dispose des droits d'écriture
-w FICHIER
Vérifier que le fichier existe et qu'il dispose des droits d'exécution
-x FICHIER
Exercice :
Saisir un nom de fichier et déterminer s'il existe.
S'il n'existe pas, l'indiquer puis le créer.
S'il existe, identifier son type parmi b, c, d, L, s, p, f et l'afficher.
S'il s'agit d'un répertoire, lister tous les fichiers .sh qu'il contient.
S'il s'agit d'un fichier ordinaire, vérifier les droits d'écriture ; si ceux-ci sont absents, ajouter les permissions lecture, écriture et exécution.
Écrire ensuite la chaîne « hello » dans le fichier.
La structure case...esac
case $variable in
motif1)
instructions
;;
motif2)
instructions
;;
...
*)
instructions_par_defaut
;;
esac
- La variable joue le rôle d'une expression de type switch.
- Chaque motif correspond à une valeur possible de cette variable.
Formats aceptés pour un motif :
hello)
'hello')
"hello")
Les motifs peuvent être reliés par un OU bit à bit :
A|a)
Ils peuvent également combiner des métacaractères : *, ?, []
h*)
h?)
9[0-9]|100)
- Le
;;joue le rôle dubreakd'un switch : il clôt la branche courante. Le dernier est facultatif, tous les autres sont obligatoires. *)capture tous les cas non couverts par les motifs précédents.
Exercice : saisir une note et déterminer la mention correspondante avec case...esac.
90-100 A / 70-89 B / 60-69 C / 0-59 D

Exrecice : saisir une expression et implémenter les opérateurs + - * / % avec case...esac.

Les boucles du shell
Le shell propose les boucles for, while et select...in.
while
Format :
while [ expression ] # ou while test expression
do
corps_de_boucle
done
Tant que l'expression est vraie, la boucle s'exécute ; dès qu'elle devient fausse, la boucle se termine.
Boucle infinie :
while [ 1 ] # ou while true
Exercices :
Calculer la factorielle de n.

Saisir un tableau, afficher chaque élément avec une boucle et calculer la moyenne.

Saisir un nombre et calculer la somme de ses chiffres.

Saisir une chaîne et l'inverser.

for
Première syntaxe (proche du C, ne permet pas d'itérer sur des éléments non contigus) :
for (( expression1 ; expression2 ; expression3 ))
do
corps_de_boucle
done
Exercice : déclarer un tableau, l'initialiser et l'afficher à l'aide d'une boucle.

Exercice : déclarer un tableau contenant les fichiers et dossiers de /home/ubuntu/ et l'afficher en boucle.
Deuxième syntaxe (proche de Python, plus polyvalente) :
for variable in liste_de_valeurs
do
corps_de_boucle
done
La liste est découpée sur les espaces par défaut.
Elle peut être composée de constantes :
for i in 1 2 3 4 5
Parcourir les éléments d'un tableau :
for element in ${monTableau[*]}
La liste peut aussi être une séquence ordonnée produite par seq :
$(seq debut pas fin)
`seq debut pas fin`
Ou par une expansion d'accolades (les deux bornes doivent être des constantes) :
{1..10}
Exercices :
Saisir un tableau et déterminer la valeur maximale.

Saisir un entier et déterminer avec une boucle s'il s'agit d'un nombre parfait (somme de ses diviseurs propres égale au nombre).

Afficher un triangle de chiffres avec for.

select...in
Cette boucle renforce l'interactivité entre le terminal et l'utilisateur.
select variable in liste_de_valeurs
do
instructions
done
Les éléments de la liste sont assignés successivement à la variable, un menu est affiché, et l'utilisateur choisit par son numéro. Si la saisie ne correspond à aucune entrée, la variable reçoit une valeur vide ; si l'utilisateur appuie simplement sur Entrée, le menu est réaffiché.
Instructions de saut
break
break n
n=1 : facultatif, sort de la boucle courante.
n différent de 1 : obligatoire, sort des n niveaux de boucles imbriquées.
continue
continue n
n=1 : facultatif, passe à l'itération suivante de la boucle courante.
n différent de 1 : obligatoire, agit sur les n niveaux de boucles imbriquées.
Exercices
1. Déclarer un tableau contenant les fichiers et dossiers de /home/ubuntu/, puis compter séparément le nombre de fichiers et de répertoires à l'aide d'une boucle.

#!/bin/bash
contenu=($(ls /home/ubuntu))
total=${#contenu[*]}
nbFichiers=0
nbDossiers=0
for (( indice=0; indice<total; indice++ ))
do
entree=${contenu[indice]}
echo "$entree"
if [ -f "$entree" ]
then
((nbFichiers++))
elif [ -d "$entree" ]
then
((nbDossiers++))
fi
done
echo "Fichiers : $nbFichiers | Dossiers : $nbDossiers"
2. Demander un identifiant et un mot de passe (saisie masquée), avec trois tentatives autorisées. Identifiant par défaut : admin, mot de passe : 1234, solde du compte : 10000.
En cas d'échec, afficher un message indiquant le numéro de la tentative et le nombre d'essais restants.
Après trois échecs, le compte est verrouillé.
En cas de succès, afficher le menu de la banque :
- Consulter le solde
- Retirer de l'argent
- Déposer de l'argent
Option 1 : afficher le solde courant.
Option 2 : demander le montant. Si celui-ci dépasse le solde, indiquer que le solde est insuffisant. Sinon, effectuer le retrait et afficher le nouveau solde.
Option 3 : demander le montant, l'ajouter au solde et afficher le nouveau solde.

#!/bin/bash
solde=10000
utilisateurAttendu="admin"
motDePasseAttendu="1234"
essais=0
verrouille=0
while [ $verrouille -eq 0 ]
do
read -p "Identifiant : " saisieUser
read -s -p "Mot de passe : " saisiePass
echo
if [ "$saisieUser" = "$utilisateurAttendu" ] && [ "$saisiePass" = "$motDePasseAttendu" ]
then
echo "Connexion réussie"
quitter=0
while [ $quitter -eq 0 ]
do
select choix in "Solde" "Retrait" "Dépôt" "Quitter"
do
case $choix in
"Solde")
echo "Solde actuel : $solde"
;;
"Retrait")
read -p "Montant à retirer : " montant
if [ $montant -gt $solde ]
then
echo "Solde insuffisant, veuillez réessayer"
else
solde=$((solde - montant))
echo "Retrait effectué. Solde restant : $solde"
fi
;;
"Dépôt")
read -p "Montant à déposer : " montant
solde=$((solde + montant))
echo "Dépôt effectué. Nouveau solde : $solde"
;;
"Quitter")
quitter=1
;;
*)
echo "Choix invalide"
;;
esac
break
done
done
else
essais=$((essais + 1))
restants=$((3 - essais))
if [ $restants -eq 0 ]
then
echo "Troisième échec, le compte est verrouillé"
verrouille=1
else
echo "Échec de la tentative $essais, il reste $restants essai(s)"
fi
fi
done
3. Saisir un tableau et le trier par la méthode du tri à bulles.

#!/bin/bash
read -p "Saisir une liste de nombres : " -a nombres
taille=${#nombres[*]}
for (( passe=0; passe<taille-1; passe++ ))
do
for (( position=0; position<taille-passe-1; position++ ))
do
voisin=$((position + 1))
if [ ${nombres[position]} -gt ${nombres[voisin]} ]
then
tampon=${nombres[position]}
nombres[position]=${nombres[voisin]}
nombres[voisin]=$tampon
fi
done
done
echo "Tableau trié : ${nombres[*]}"