RousselTMLEARNING

Systèmes & Linux

Scripts Bash pour l'administrateur : écrire des scripts robustes

Automatiser l'administration Linux avec Bash : variables et guillemets, conditions, boucles, fonctions, codes de retour, set -euo pipefail, trap, arguments avec getopts, journalisation, ShellCheck et planification.

1. Les bases #

Un script, c'est une suite de commandes.

Squelette #

Structure d'un script Bash
Toujours la même organisation : on retrouve vite ce que l'on cherche.
#!/usr/bin/env bash
# backup-etc.sh : archive /etc dans /var/backups
set -euo pipefail

readonly DEST=/var/backups
readonly STAMP=$(date +%F_%H%M)

main() {
    mkdir -p "$DEST"
    tar -czf "$DEST/etc_$STAMP.tar.gz" /etc
    echo "Archive créée : $DEST/etc_$STAMP.tar.gz"
}

main "$@"
chmod +x backup-etc.sh && sudo ./backup-etc.sh
bash -x backup-etc.sh   # exécuter en traçant chaque commande
OptionEffet
set -earrête le script à la première commande en échec
set -uerreur si une variable n'est pas définie
set -o pipefailun pipe échoue si une de ses commandes échoue

Variables et guillemets #

nom="web 01"
echo "$nom"          # guillemets doubles : la variable est développée, les espaces préservés
echo '$nom'          # guillemets simples : texte littéral
fichiers=$(ls /etc)  # substitution de commande
echo "${nom:-defaut}"     # valeur par défaut
echo "${#nom}"            # longueur
echo "${fichier%.log}.gz" # remplacer un suffixe
(( total = 3 * 7 )) ; echo "$total" 
Bonne Pratique : Mettez toujours les variables entre guillemets doubles ("$var") : sans eux, un nom de fichier avec un espace devient deux arguments.

2. Contrôle de flux #

Conditions, boucles, fonctions.

Conditions #

if [[ -f /etc/nginx/nginx.conf ]]; then echo "nginx configuré"; fi
if ! systemctl is-active --quiet nginx; then
    systemctl restart nginx
fi
[[ $EUID -eq 0 ]] || { echo "À lancer en root" >&2; exit 1; }
[[ $usage -gt 90 && $env == prod ]] && alerte
case "$1" in
    start|stop) systemctl "$1" app ;;
    *) echo "Usage : $0 {start|stop}" >&2; exit 2 ;;
esac

Tests utiles #

TestVrai si
-f / -d / -efichier / répertoire / existe
-r -w -xlisible, modifiable, exécutable
-z / -nchaîne vide / non vide
-eq -ne -lt -gtcomparaisons numériques
== !=, =~comparaison de chaînes, expression régulière

Boucles #

for host in web01 web02 db01; do
    ssh "$host" uptime
done
for f in /var/log/app/*.log; do gzip "$f"; done
while read -r user shell; do
    echo "$user utilise $shell"
done < <(awk -F: '$3>=1000 {print $1, $7}' /etc/passwd)
for i in {1..5}; do curl -sf http://localhost:8080/health && break; sleep 2; done

Fonctions et codes de retour #

log() { echo "$(date '+%F %T') [$1] ${*:2}" | tee -a /var/log/monscript.log; }
check_disk() {
    local usage
    usage=$(df --output=pcent / | tail -1 | tr -dc '0-9')
    (( usage < ${1:-90} ))      # code retour 0 si OK
}
if ! check_disk 85; then log WARN "disque racine presque plein"; fi
echo $?   # code de retour de la dernière commande (0 = succès)

Un script doit renvoyer 0 en cas de succès et un code non nul en cas d'erreur : cron, systemd, Ansible et les pipelines CI s'en servent pour détecter l'échec.

Codes de retour
Un script qui renvoie toujours 0 rend ses échecs invisibles.

3. Rendre un script robuste #

Ce qui distingue un script de production.

trap, verrou et arguments #

#!/usr/bin/env bash
set -euo pipefail
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT                       # nettoyage, même en cas d'erreur
trap 'echo "Erreur ligne $LINENO" >&2' ERR

exec 9>/run/lock/backup.lock
flock -n 9 || { echo "Déjà en cours" >&2; exit 1; }   # empêcher deux exécutions

DRY_RUN=0 ; RETENTION=7
while getopts "nr:h" opt; do
    case $opt in
        n) DRY_RUN=1 ;;
        r) RETENTION=$OPTARG ;;
        h|*) echo "Usage : $0 [-n] [-r jours]"; exit 0 ;;
    esac
done

Qualité #

ShellCheck
ShellCheck repère les guillemets manquants et les commandes dangereuses.
  • Analysez chaque script avec ShellCheck (aussi en CI).
  • Rendez les scripts idempotents : les relancer ne doit rien casser (mkdir -p, tester avant de créer).
  • Proposez un mode simulation (-n) pour les actions destructrices.
  • Journalisez avec horodatage, sur stderr pour les erreurs.
  • Versionnez les scripts dans Git ; au-delà de ~200 lignes ou de logique complexe, passez à Python ou à Ansible.

Planifier #

Un script de maintenance se lance avec un timer systemd (journalisation, rattrapage, dépendances) ou cron. Testez-le d'abord avec l'environnement minimal : env -i /bin/bash --noprofile --norc ./script.sh.

Articles liés

Architecture d'un système Linux : noyau, démarrage et arborescence

Comprendre comment fonctionne un serveur Linux : noyau et espace utilisateur, séquence de démarrage UEFI, GRU…

Le shell Bash et les fichiers : navigation, redirections, pipes et traitement de texte

Maîtriser la ligne de commande Linux : chemins, manipulation de fichiers, jokers, redirections, pipes, recher…

Utilisateurs, groupes, sudo et permissions sous Linux

Gérer les comptes Linux (useradd, usermod, passwd, chage), les groupes, sudo et visudo, les permissions rwx e…

Pour aller plus loin