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
$?
L'objectif RHCSA « Create simple shell scripts » (EX200, RHEL 10) cite deux points que cette page couvre directement : traiter les arguments du script ($1, $2) et exploiter la sortie des commandes ($( )). L'arithmétique et les tableaux n'y figurent pas : ils servent vos scripts, pas l'examen.
Variables simples
Section intitulée « Variables simples »Une variable se déclare sans espaces autour du =. Le nom commence par une lettre ou un souligné, et la valeur se met entre guillemets dès qu'elle contient un espace :
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 la désactivent, et le texte reste littéral :
echo "$nom" # Aliceecho '$nom' # $nom (littéral)Substitution de commande
Section intitulée « Substitution de commande »$() exécute une commande et capture sa sortie, que l'on range dans une variable :
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 paramètres spéciaux sont définis automatiquement par Bash, et aucun ne s'affecte avec = : 0=x ou 1=x répondent command not found. Seuls les arguments positionnels se remplacent, en bloc, avec set -- valeur1 valeur2. Le tableau se lit ligne par ligne, avec l'exemple d'un script appelé avec deux arguments.
| 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 n'apparaît qu'entre guillemets doubles. set -- remplace les arguments positionnels du shell courant, ce qui simule un script appelé avec "avec espace" et arg2 :
set -- "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 : chaque argument reste un mot distinct, espaces internes compris. "$*" fusionne tous les arguments en une seule chaîne, séparés par un espace, et la boucle ne fait plus qu'un tour.
Arithmétique entière
Section intitulée « Arithmétique entière »$(( expression )) évalue une expression arithmétique sur des entiers, sans appeler expr ni aucun processus externe :
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 : son code de retour vaut toujours 0, ce qui la rend compatible avec set -e, le mode qui arrête le script à la première commande en échec :
compteur=0compteur=$((compteur + 1))compteur=$((compteur + 5))echo "$compteur" # 6Bash ne gère que les entiers. Pour un résultat décimal, on délègue à bc ou à awk. bc n'est pas toujours installé : il manque aux images debian:13 et almalinux:10 mesurées, alors que awk est présent partout.
echo "scale=2; 10 / 3" | bc # 3.33, si bc est installéawk 'BEGIN { printf "%.2f\n", 10 / 3 }' # 3.33, avec awkExpansion de paramètre
Section intitulée « Expansion de paramètre »L'expansion de paramètre ${ } offre bien plus que la simple substitution : valeur par défaut, découpage de chemin, changement de casse, remplacement, le tout sans lancer de processus.
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/localLes quatre formes ne diffèrent que d'un caractère. Le tableau ci-dessous est celui à garder sous la main : :- et := fournissent un repli, :+ une alternative, :? une garde qui arrête le script.
| 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, le script utilise les défauts. Appelé avec DEST=/mnt/nas ./script.sh, il utilise /mnt/nas : la variable est posée dans l'environnement de cette seule commande, sans rien modifier à votre session.
Manipulation de chaînes
Section intitulée « Manipulation de chaînes »Sans appeler sed ou awk, Bash peut transformer des chaînes directement. Les résultats ci-dessous ont été vérifiés sur Bash 5.2, avec un chemin de journal nginx :
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)La même syntaxe, résumée. Retenez la règle du signe doublé : ## et %% retirent la correspondance la plus longue, # et % la plus courte ; # agit par le début, % par la fin.
| 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 indexé stocke plusieurs valeurs accessibles par index entier, qui 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)Pour parcourir tous les éléments, bouclez sur "${serveurs[@]}", guillemets compris, pour que chaque valeur reste entière :
for s in "${serveurs[@]}"; do echo " → $s"done# → web01# → web02# → db01# → cache01Pour ajouter un élément à la fin du tableau, utilisez += avec des parenthèses :
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. L'ordre d'itération des clés n'est pas celui de l'insertion : Bash 5.2 rend ici ssh https mysql http, et rien ne garantit cet ordre d'une version à l'autre.
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 80Pour parcourir clés et valeurs ensemble, bouclez sur les clés et lisez chaque valeur avec ${ports[$service]} :
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 »export place une variable dans l'environnement transmis à chaque processus enfant : les programmes lancés par votre script peuvent alors la lire.
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, y compris un bash -c lancé depuis le script :
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, avec des effets de bord difficiles à suivre :
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 »La plupart des erreurs de variables se reconnaissent à leur message, relevé ici sur Bash 5.2 (Debian 13 et AlmaLinux 10). Deux causes reviennent sans cesse : un espace autour du = et un script lancé par sh au lieu de Bash.
| Symptôme | Cause probable | Solution |
|---|---|---|
nom: command not found et variable 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 |
| Un argument contenant un espace arrive coupé en deux | $@ utilisé sans guillemets | Toujours écrire "$@" |
(( expr )) stoppe le script avec set -e | Résultat arithmétique = 0 | Utiliser var=$((var + 1)) |
declare: not found (dash) ou declare: -A: invalid option (Bash 3) | Script lancé avec sh, ou Bash antérieur à 4.0 | Lancer avec bash ou ./script.sh, vérifier bash --version |
À 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.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Conditions et tests Bash
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications