Les variables Bash ne sont pas des types. Une variable est une chaîne de caractères — et l’interprétation comme nombre, tableau ou chemin dépend du contexte dans lequel vous l’utilisez. Comprendre ce modèle simple mais puissant vous permet d’exploiter l’expansion de paramètre, l’arithmétique entière et les tableaux pour écrire des scripts concis et robustes.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Déclarer des variables et interpréter les variables spéciales
$?,$#,$@,$0,$$ - Calculer avec
$(( ))sans appelerbcouexpr - Appliquer des valeurs par défaut avec
${var:-valeur}pour les scripts configurables - Extraire et transformer des chaînes sans awk ni sed
- Créer des tableaux indexés et associatifs pour grouper des données
- Contrôler la portée d’une variable avec
exportetlocal
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Les variables et leurs expansions interviennent dans presque tous les scripts de niveau intermédiaire :
- lire la configuration depuis des variables d’environnement avec une valeur par défaut si elles ne sont pas définies (
${TIMEOUT:-30}) - extraire le nom de fichier ou l’extension d’un chemin sans appeler
basename - compter les arguments passés à un script et afficher un usage si
$# -lt 2 - stocker la liste des serveurs à traiter dans un tableau et itérer dessus
- vérifier le code de retour de la dernière commande avec
$?
En préparation RHCSA, les variables spéciales ($1, $#, $@, $?) et l’arithmétique entière sont testées explicitement dans les exercices de scripting.
Variables simples
Section intitulée « Variables simples »Une variable se déclare sans espaces autour du = :
nom="Alice"age=30chemin="/var/log/nginx"echo "Nom : $nom, Age : $age"# Nom : Alice, Age : 30Les guillemets doubles permettent l’expansion des variables. Les guillemets simples les désactivent :
echo "$nom" # Aliceecho '$nom' # $nom (littéral)Substitution de commande
Section intitulée « Substitution de commande »$() exécute une commande et capture sa sortie :
date_iso=$(date '+%Y-%m-%d')user_actuel=$(whoami)nb_fichiers=$(ls /etc | wc -l)
echo "Date : $date_iso, Utilisateur : $user_actuel, Fichiers dans /etc : $nb_fichiers"# Date : 2026-04-10, Utilisateur : bob, Fichiers dans /etc : 222Variables spéciales
Section intitulée « Variables spéciales »Ces variables sont définies automatiquement par Bash — vous ne pouvez pas les affecter directement (sauf $0 qui est en lecture seule).
| Variable | Signification | Exemple |
|---|---|---|
$0 | Nom du script | demo-variables.sh |
$1 … $9 | Arguments positionnels | $1 = premier argument |
$# | Nombre d’arguments | 2 si deux args passés |
$@ | Tous les arguments (liste) | "un" "deux" |
$* | Tous les arguments (chaîne) | "un deux" |
$? | Code de retour du dernier programme | 0 = succès |
$$ | PID du processus courant | 12345 |
$! | PID du dernier processus en arrière-plan | 12346 |
# Appelé : ./script.sh un deuxecho "Script : $0" # Script : ./script.shecho "Premier : $1" # Premier : unecho "Nb args : $#" # Nb args : 2echo "Tous : $@" # Tous : un deux
ls /inexistant 2>/dev/nullecho "Code retour : $?" # Code retour : 2$@ vs $*
Section intitulée « $@ vs $* »La différence est visible entre guillemets doubles :
args=("avec espace" "arg2")
for a in "$@"; do echo "→ $a"; done# → avec espace# → arg2
for a in "$*"; do echo "→ $a"; done# → avec espace arg2 (fusionné en un seul argument)Utilisez toujours "$@" pour passer ou parcourir les arguments — "$*" écrase les espaces dans les valeurs.
Arithmétique entière
Section intitulée « Arithmétique entière »$(( expression )) évalue une expression arithmétique entière :
a=10; b=3echo "$((a + b))" # 13echo "$((a - b))" # 7echo "$((a * b))" # 30echo "$((a / b))" # 3 (division entière)echo "$((a % b))" # 1 (modulo)echo "$((a ** b))" # 1000 (puissance)Pour incrémenter une variable, préférez l’affectation explicite (compatible set -e) :
compteur=0compteur=$((compteur + 1))compteur=$((compteur + 5))echo "$compteur" # 6Bash ne gère que les entiers. Pour les calculs décimaux :
echo "scale=2; 10 / 3" | bc # 3.33Expansion de paramètre
Section intitulée « Expansion de paramètre »L’expansion de paramètre ${ } offre bien plus que la simple substitution.
Valeurs par défaut
Section intitulée « Valeurs par défaut »# Utiliser la valeur si définie, sinon la valeur par défautTIMEOUT=${TIMEOUT:-30}PREFIX=${PREFIX:-"/usr/local"}
echo "Timeout : $TIMEOUT" # Timeout : 30 (si TIMEOUT non défini)echo "Prefix : $PREFIX" # Prefix : /usr/local| Syntaxe | Comportement |
|---|---|
${var:-défaut} | Retourne défaut si var est vide ou non définie |
${var:=défaut} | Affecte et retourne défaut si var est vide ou non définie |
${var:+valeur} | Retourne valeur si var est définie et non vide |
${var:?message} | Erreur avec message si var est vide ou non définie |
# Exemple pratique : rendre un script configurable par l'environnement#!/bin/bashset -euo pipefail
DEST=${DEST:-"/tmp/backup"}KEEP=${KEEP:-7}LOG=${LOG:-"/var/log/backup.log"}
echo "Sauvegarde vers $DEST, conservation $KEEP jours, log $LOG"Appelé sans variables : utilise les défauts. Appelé avec DEST=/mnt/nas ./script.sh : utilise /mnt/nas.
Manipulation de chaînes
Section intitulée « Manipulation de chaînes »Sans appeler sed ou awk, Bash peut transformer des chaînes directement :
chemin="/var/log/nginx/access.log"
echo "${#chemin}" # 25 (longueur)echo "${chemin^^}" # /VAR/LOG/NGINX/ACCESS.LOG (majuscules)echo "${chemin,,}" # /var/log/nginx/access.log (minuscules)echo "${chemin##*/}" # access.log (tout ce qui est après le dernier /)echo "${chemin%/*}" # /var/log/nginx (tout ce qui est avant le dernier /)echo "${chemin##*.}" # log (extension)echo "${chemin%.log}.bak" # /var/log/nginx/access.bak (remplace l'extension)echo "${chemin/nginx/apache}" # /var/log/apache/access.log (première occurrence)Tableau récapitulatif :
| Syntaxe | Résultat |
|---|---|
${#var} | Longueur de la chaîne |
${var^^} | Tout en majuscules |
${var,,} | Tout en minuscules |
${var##motif} | Supprime le plus long préfixe correspondant au motif |
${var%%motif} | Supprime le plus long suffixe correspondant au motif |
${var#motif} | Supprime le plus court préfixe |
${var%motif} | Supprime le plus court suffixe |
${var/motif/remp} | Remplace la première occurrence |
${var//motif/remp} | Remplace toutes les occurrences |
Tableaux indexés
Section intitulée « Tableaux indexés »Un tableau stocke plusieurs valeurs accessibles par index entier (commence à 0) :
serveurs=("web01" "web02" "db01" "cache01")
echo "${serveurs[0]}" # web01echo "${serveurs[-1]}" # cache01 (dernier élément)echo "${#serveurs[@]}" # 4 (nombre d'éléments)echo "${serveurs[@]}" # web01 web02 db01 cache01echo "${serveurs[@]:1:2}" # web02 db01 (slice : 2 éléments à partir de l'index 1)Parcourir tous les éléments :
for s in "${serveurs[@]}"; do echo " → $s"done# → web01# → web02# → db01# → cache01Ajouter un élément :
serveurs+=("monitor01")echo "${serveurs[@]}" # web01 web02 db01 cache01 monitor01Tableaux associatifs
Section intitulée « Tableaux associatifs »Les tableaux associatifs (declare -A) utilisent des clés textuelles — l’équivalent d’un dictionnaire :
declare -A portsports["ssh"]=22ports["http"]=80ports["https"]=443ports["mysql"]=3306
echo "${ports[ssh]}" # 22echo "${!ports[@]}" # Toutes les clés : ssh https mysql httpecho "${ports[@]}" # Toutes les valeurs : 22 443 3306 80Parcourir clés et valeurs :
for service in "${!ports[@]}"; do echo " $service → ${ports[$service]}"done# ssh → 22# https → 443# mysql → 3306# http → 80Export et portée
Section intitulée « Export et portée »export — rendre une variable visible aux processus enfants
Section intitulée « export — rendre une variable visible aux processus enfants »LANG=fr_FR.UTF-8export LANG
# Équivalent en une ligne :export LANG=fr_FR.UTF-8Une variable non exportée n’est pas transmise aux sous-processus :
MA_VAR="bonjour"bash -c 'echo $MA_VAR' # (vide — non transmis)
export MA_VARbash -c 'echo $MA_VAR' # bonjourlocal — limiter la portée à une fonction
Section intitulée « local — limiter la portée à une fonction »Sans local, toute affectation dans une fonction modifie la variable globale :
GLOBAL="initial"
ma_fonction() { local LOCAL="je suis local" GLOBAL="modifié dans la fonction" echo "Dans la fonction : $LOCAL / $GLOBAL"}
ma_fonctionecho "Après : $GLOBAL" # modifié dans la fonction# echo $LOCAL # bash: LOCAL: unbound variable (avec set -u)Règle : dans toute fonction, déclarez les variables internes avec local pour éviter les effets de bord.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| Variable toujours vide | Espace autour du = | var=valeur sans espaces |
: unbound variable avec set -u | Variable non initialisée | Initialiser ou utiliser ${var:-} comme défaut vide |
$@ fusionne des valeurs avec espaces | Utilisé sans guillemets | Toujours écrire "$@" |
(( expr )) stoppe le script avec set -e | Résultat arithmétique = 0 | Utiliser var=$((var + 1)) |
| Tableau associatif non reconnu | Bash < 4.0 ou sh au lieu de bash | Vérifier bash --version et le shebang #!/bin/bash |
À retenir
Section intitulée « À retenir »$?est le code de retour du dernier programme — à lire immédiatement, une commande suivante l’écrase."$@"passe les arguments correctement en préservant les espaces ;"$*"les fusionne.${var:-défaut}rend les scripts configurables sans conditionif.${chemin##*/}extrait le nom de fichier ;${chemin%/*}extrait le répertoire — sans appelerbasename/dirname.declare -Acrée un tableau associatif — disponible uniquement avec Bash 4+.- Déclarez toujours les variables internes à une fonction avec
local.