Bash : variables spéciales, codes de retour et paramètres positionnels
Les variables spéciales du shell Bash et les bons réflexes pour ne plus jamais faire planter ses scripts ;) !
Dans l'écriture de scripts Bash au quotidien, certaines variables magiques reviennent tout le temps mais finissent souvent par être confondues. Comprendre exactement ce qu'elles contiennent évite bien des maux de tête et permet de blinder ses scripts.
Les variables spéciales en un coup d'œil
| Variable | Ce qu'elle contient | Exemple d'utilisation |
|---|---|---|
$? |
Code de retour de la dernière commande (0 = succès, autre = erreur). | if [ $? -ne 0 ]; then echo "Erreur"; fi |
$0 |
Nom du script en cours d'exécution (ou du shell). | echo "Usage: $0 [fichier]" |
$1, $2.. |
Paramètres positionnels passés au script (${10} au-delà de 9). |
DOSSIER="$1" |
$# |
Nombre total de paramètres reçus. | if [ $# -lt 2 ]; then exit 1; fi |
"$@" |
Tous les paramètres sous forme de mots séparés distincts. | for arg in "$@"; do ... done |
"$*" |
Tous les paramètres fusionnés en une seule chaîne. | echo "Reçu : $*" |
$$ |
PID du shell en cours d'exécution. | Fichier temporaire unique : /tmp/job_$$.tmp |
$! |
PID du dernier processus lancé en tâche de fond avec &. |
serveur & ; PID_APP=$! |
$_ |
Dernier argument de la commande précédente. | mkdir -p /opt/app && cd $_ |
$- |
Drapeaux et options actuellement actifs dans le shell. | echo $- |
Le piège classique : "$@" contre "$*"
C'est l'un des bugs les plus fréquents dès qu'un argument contient des espaces.
Prenons un script exécuté avec :
./script.sh "mon fichier.txt" "destination.txt"
"$@"(avec guillemets) conserve fidèlement le découpage :"$1"et"$2"➔ 2 arguments distincts (mon fichier.txtetdestination.txt). C'est presque toujours ce que vous voulez utiliser."$*"(avec guillemets) fusionne tout en un seul bloc textuel :"mon fichier.txt destination.txt"➔ 1 seul gros argument.$@ou$*(sans guillemets) redécoupe chaque mot sur les espaces : ➔ 3 arguments cassés (mon,fichier.txt,destination.txt).
Le piège du code de retour $?
La variable $? est volatile : elle est immédiatement écrasée par la prochaine commande exécutée, même par un simple echo !
# PIÈGE : le deuxième test testera la réussite du 'echo', pas du ping !
ping -c 1 1.1.1.1
if [ $? -ne 0 ]; then
echo "Hôte injoignable !"
# Si on réutilise $? ici, il vaudra 0 parce que le echo a fonctionné...
fi
# BONNE PRATIQUE : stocker immédiatement le code dans une variable
ping -c 1 1.1.1.1
CODE_RETOUR=$?
if [ $CODE_RETOUR -ne 0 ]; then
echo "Échec de connexion (code : $CODE_RETOUR)"
exit $CODE_RETOUR
fi
La formule magique pour blinder ses scripts
Plutôt que de tester manuellement chaque code de retour, prenez l'habitude de placer cette ligne au tout début de vos scripts :
#!/usr/bin/env bash
set -euo pipefail
-e: Arrête immédiatement le script si une commande échoue.-u: Provoque une erreur si vous utilisez une variable non définie (évite les suppressions catastrophiques du stylerm -rf "$DOSSIER_VIDE/*").-o pipefail: Dans un pipeline (cmd1 | cmd2), sicmd1plante, l'ensemble du pipeline est considéré en échec (par défaut Bash n'aurait regardé quecmd2).
Exemple de squelette propre
#!/usr/bin/env bash
set -euo pipefail
# On vérifie qu'on a bien reçu l'argument requis
if [ $# -lt 1 ]; then
echo "Usage: $0 `[nom_du_fichier]`" >&2
exit 1
fi
FICHIER="$1"
TMP_FILE="/tmp/traitement_$$.tmp"
# Nettoyage automatique du fichier temporaire à la sortie du script
trap 'rm -f "$TMP_FILE"' EXIT
echo "Traitement de $FICHIER (PID: $$)..."
touch "$TMP_FILE"
En résumé
- Entourez toujours vos variables de guillemets et utilisez
"$@"pour passer des listes d'arguments. - Isolez
$?dans une variable locale si vous en avez besoin sur plusieurs lignes. - Le mode strict
set -euo pipefailest le meilleur moyen d'avoir des scripts fiables dès le départ ;)