Bash est omniprésent : déploiements, cron jobs, scripts d’init, hooks Git, pipelines CI. Un script Bash mal écrit fonctionne jusqu’au jour où il détruit quelque chose en production. Voici les bases pour écrire des scripts fiables.
Le shebang et les options de sécurité
Chaque script commence par deux lignes qui changent tout :
#!/usr/bin/env bash
set -euo pipefail
set -e: arrête le script dès qu’une commande retourne un code non-zéroset -u: erreur si une variable non définie est utiliséeset -o pipefail: un pipe échoue si n’importe quelle commande du pipe échoue
Sans ces options, un script peut silencieusement ignorer des erreurs et continuer à s’exécuter dans un état incohérent.
Variables : typage et portée
#!/usr/bin/env bash
set -euo pipefail
# Constantes en majuscules
readonly DEPLOY_DIR="/opt/myapp"
readonly LOG_FILE="/var/log/deploy.log"
readonly TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Variables locales dans les fonctions
process_file() {
local file="$1" # local = scope limité à la fonction
local basename
basename=$(basename "$file")
echo "Traitement : $basename"
}
# Valeur par défaut si variable non définie
ENVIRONMENT="${ENVIRONMENT:-staging}"
MAX_RETRIES="${MAX_RETRIES:-3}"
Toujours mettre les variables entre guillemets doubles ("$var") pour éviter les problèmes avec les espaces et les caractères spéciaux.
Fonctions : structurer le code
#!/usr/bin/env bash
set -euo pipefail
# Logging standardisé
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] INFO $*" | tee -a "$LOG_FILE"; }
warn() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] WARN $*" | tee -a "$LOG_FILE" >&2; }
error() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR $*" | tee -a "$LOG_FILE" >&2; exit 1; }
check_prerequisites() {
local missing=0
for cmd in docker kubectl helm; do
if ! command -v "$cmd" &>/dev/null; then
warn "Commande manquante : $cmd"
missing=$((missing + 1))
fi
done
[[ $missing -eq 0 ]] || error "Prérequis manquants. Abandon."
}
deploy_application() {
local image="$1"
local namespace="${2:-default}"
log "Déploiement de $image dans $namespace"
kubectl set image deployment/myapp app="$image" -n "$namespace"
kubectl rollout status deployment/myapp -n "$namespace" --timeout=5m
log "Déploiement terminé"
}
Gestion des erreurs et nettoyage
#!/usr/bin/env bash
set -euo pipefail
TMPDIR=$(mktemp -d)
# Cleanup automatique à la sortie du script (succès ou erreur)
cleanup() {
local exit_code=$?
rm -rf "$TMPDIR"
if [[ $exit_code -ne 0 ]]; then
error "Le script a échoué avec le code $exit_code"
fi
}
trap cleanup EXIT
# Traiter les signaux (Ctrl+C, kill)
trap 'warn "Interruption reçue"; exit 130' INT TERM
# Retry automatique pour les opérations réseau
retry() {
local max_attempts="$1"
local delay="$2"
shift 2
local attempt=1
until "$@"; do
if [[ $attempt -ge $max_attempts ]]; then
error "Échec après $max_attempts tentatives : $*"
fi
warn "Tentative $attempt/$max_attempts échouée, retry dans ${delay}s"
sleep "$delay"
attempt=$((attempt + 1))
done
}
# Utilisation
retry 3 5 curl -sf https://api.example.com/health
Boucles et traitement de fichiers
# Itérer sur des fichiers — toujours avec find, jamais ls
while IFS= read -r -d '' file; do
log "Traitement : $file"
process_file "$file"
done < <(find /data -name "*.csv" -newer /tmp/last_run -print0)
# Lire un fichier ligne par ligne
while IFS= read -r line; do
[[ -z "$line" || "$line" == \#* ]] && continue # ignorer vides et commentaires
echo "Ligne : $line"
done < config.txt
# Tableau de serveurs
declare -a SERVERS=("web01" "web02" "web03" "db01")
for server in "${SERVERS[@]}"; do
ssh -o ConnectTimeout=5 "$server" "systemctl status myapp" || warn "$server : non joignable"
done
Validation des arguments
usage() {
cat << USAGE
Usage: $(basename "$0") [OPTIONS] <environment>
OPTIONS:
-i, --image IMAGE Image Docker à déployer
-n, --dry-run Simuler sans effectuer de changements
-h, --help Afficher cette aide
EXEMPLES:
$(basename "$0") production -i myapp:1.2.3
$(basename "$0") staging --dry-run
USAGE
exit 1
}
# Parser les arguments
DRY_RUN=false
IMAGE=""
ENVIRONMENT=""
while [[ $# -gt 0 ]]; do
case "$1" in
-i|--image) IMAGE="$2"; shift 2 ;;
-n|--dry-run) DRY_RUN=true; shift ;;
-h|--help) usage ;;
-*) error "Option inconnue : $1" ;;
*) ENVIRONMENT="$1"; shift ;;
esac
done
[[ -n "$ENVIRONMENT" ]] || error "L'environnement est requis"
[[ -n "$IMAGE" ]] || error "L'image Docker est requise"
[[ "$ENVIRONMENT" =~ ^(staging|production)$ ]] || error "Environnement invalide : $ENVIRONMENT"
Notre formation Linux couvre Bash scripting en profondeur avec des ateliers sur des cas réels d’administration système.