sauvegarde_rsync.sh

Note de lecture générée automatiquement

Le vrai fichier est scripts/sauvegarde/sauvegarde_rsync.sh (Obsidian n’affiche pas les scripts eux-mêmes). Ne modifie pas cette note : modifie le script, puis relance python3 generer_vues_obsidian.py dans le dossier scripts.

Code

#!/usr/bin/env bash
# =============================================================================
# sauvegarde_rsync.sh — Sauvegarde incrémentale "par instantanés" avec rsync
#
#   Chaque exécution crée un dossier daté (AAAA-MM-JJ_HHhMM). Les fichiers qui
#   n'ont pas changé ne sont PAS recopiés : ce sont des liens physiques vers la
#   sauvegarde précédente (--link-dest). Chaque dossier paraît donc complet
#   (restauration facile) mais ne prend que la place des fichiers modifiés.
#   Les N plus anciens instantanés sont supprimés (rotation).
#
# Usage   : ./sauvegarde_rsync.sh <dossier_source> <dossier_destination> [nb_a_garder]
# Exemple : ./sauvegarde_rsync.sh "$HOME/obsidian/truc non trier" /media/disque_usb/vault 7
# Auto    : crontab -e  puis  0 20 * * * /chemin/sauvegarde_rsync.sh "/src" "/dest" 7
# =============================================================================
set -euo pipefail
 
source_a_sauver="${1:?Usage : $0 <source> <destination> [nb_a_garder]}"
destination="${2:?Destination manquante}"
nb_a_garder="${3:-7}"
 
-d "$source_a_sauver" || { echo "Source introuvable : $source_a_sauver" >&2; exit 1; }
mkdir -p "$destination"
 
horodatage="$(date +%Y-%m-%d_%Hh%M)"
nouvel_instantane="$destination/$horodatage"
dernier_instantane="$(find "$destination" -mindepth 1 -maxdepth 1 -type d -name '20*' | sort | tail -n 1)"
 
options_rsync=(-a --delete --human-readable --info=stats1)
if -n "$dernier_instantane"; then
  options_rsync+=(--link-dest="$dernier_instantane")
  echo "Incrémentale par rapport à : $dernier_instantane"
else
  echo "Première sauvegarde : copie complète."
fi
 
# Le "/" final sur la source = copier le CONTENU du dossier
rsync "${options_rsync[@]}" "${source_a_sauver%/}/" "$nouvel_instantane/"
 
# Rotation : on supprime les plus vieux au-delà de nb_a_garder
mapfile -t instantanes < <(find "$destination" -mindepth 1 -maxdepth 1 -type d -name '20*' | sort)
nb_a_supprimer=$(( ${#instantanes[@]} - nb_a_garder ))
if (( nb_a_supprimer > 0 )); then
  for ancien in "${instantanes[@]:0:nb_a_supprimer}"; do
    echo "Rotation : suppression de $ancien"
    rm -rf -- "$ancien"
  done
fi
 
echo "Sauvegarde terminée : $nouvel_instantane"
du -sh "$destination"/20* 2>/dev/null | tail -n "$nb_a_garder"