This article is available in French.
1. Les bases #
Un script, c'est une suite de commandes.
Squelette #
#!/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| Option | Effet |
|---|---|
set -e | arrête le script à la première commande en échec |
set -u | erreur si une variable n'est pas définie |
set -o pipefail | un 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 ;;
esacTests utiles #
| Test | Vrai si |
|---|---|
-f / -d / -e | fichier / répertoire / existe |
-r -w -x | lisible, modifiable, exécutable |
-z / -n | chaîne vide / non vide |
-eq -ne -lt -gt | comparaisons 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; doneFonctions 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.
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
doneQualité #
- 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.
My progress
Sign in to track your progress and take notes. Sign in
Recommended Articles
Package managers
The different package managers for Kubernetes and their advantages.