137 lines
3.5 KiB
Bash
137 lines
3.5 KiB
Bash
|
|
#!/usr/bin/env bash
|
|||
|
|
set -Eeuo pipefail
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# Konfiguration
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
SRC="${HOME}/"
|
|||
|
|
DEST_BASE="/run/media/hans/usbsicherung/jacboy_sicherung"
|
|||
|
|
HOST="$(hostname -s)"
|
|||
|
|
STAMP="$(date +%F_%H-%M-%S)"
|
|||
|
|
SNAPSHOT_DIR="${DEST_BASE}/${HOST}/${STAMP}"
|
|||
|
|
LAST_LINK="${DEST_BASE}/${HOST}/latest"
|
|||
|
|
LOG_DIR="${DEST_BASE}/${HOST}/logs"
|
|||
|
|
LOG_FILE="${LOG_DIR}/backup-${STAMP}.log"
|
|||
|
|
|
|||
|
|
# Anzahl der aufzubewahrenden Snapshots
|
|||
|
|
KEEP=30
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# Hilfsfunktionen
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
log() {
|
|||
|
|
# Schreibt gleichzeitig auf stdout und ins Log (wenn LOG_FILE existiert)
|
|||
|
|
local msg="[$(date +%F\ %T)] $*"
|
|||
|
|
echo "${msg}"
|
|||
|
|
if [[ -n "${LOG_FILE:-}" ]]; then
|
|||
|
|
mkdir -p "$(dirname "${LOG_FILE}")"
|
|||
|
|
echo "${msg}" >> "${LOG_FILE}"
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fail() {
|
|||
|
|
log "FEHLER: $*"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# Vorprüfungen
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
# 1. Zielbasis muss existieren und beschreibbar sein
|
|||
|
|
if [[ ! -d "${DEST_BASE}" ]]; then
|
|||
|
|
fail "Zielbasis ${DEST_BASE} existiert nicht oder ist nicht gemountet. Bitte USB-Laufwerk prüfen."
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [[ ! -w "${DEST_BASE}" ]]; then
|
|||
|
|
fail "Zielbasis ${DEST_BASE} ist nicht beschreibbar. Rechte/Mount prüfen."
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 2. Snapshot- und Log-Verzeichnisse anlegen
|
|||
|
|
mkdir -p "${SNAPSHOT_DIR}" "${LOG_DIR}"
|
|||
|
|
|
|||
|
|
log "Starte Backup: ${SRC} -> ${SNAPSHOT_DIR}"
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# rsync-Optionen
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
RSYNC_OPTS=(
|
|||
|
|
-aHAXx
|
|||
|
|
--numeric-ids
|
|||
|
|
--delete
|
|||
|
|
--delete-excluded
|
|||
|
|
--info=stats2,progress2
|
|||
|
|
--human-readable
|
|||
|
|
--partial
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
EXCLUDES=(
|
|||
|
|
--exclude=".cache/"
|
|||
|
|
--exclude="Downloads/"
|
|||
|
|
--exclude=".local/share/Trash/"
|
|||
|
|
--exclude=".gvfs/"
|
|||
|
|
--exclude=".dotnet/"
|
|||
|
|
--exclude=".codegpt/"
|
|||
|
|
--exclude=".copilot/"
|
|||
|
|
--exclude=".var/"
|
|||
|
|
--exclude=".vscode/"
|
|||
|
|
#--exclude="temp/"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Wenn es bereits einen gültigen "latest"-Snapshot gibt, diesen als link-dest verwenden
|
|||
|
|
if [[ -L "${LAST_LINK}" ]] && [[ -d "$(readlink -f "${LAST_LINK}")" ]]; then
|
|||
|
|
RSYNC_OPTS+=(--link-dest="$(readlink -f "${LAST_LINK}")")
|
|||
|
|
log "Verwende link-dest: $(readlink -f "${LAST_LINK}")"
|
|||
|
|
else
|
|||
|
|
log "Kein gültiger letzter Snapshot gefunden – es wird ein vollständiger Lauf erstellt."
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# rsync-Lauf
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
# Optional: Dry-Run zum Testen (auskommentieren, wenn du real sichern willst)
|
|||
|
|
# RSYNC_OPTS+=(--dry-run)
|
|||
|
|
|
|||
|
|
log "Starte rsync..."
|
|||
|
|
if rsync "${RSYNC_OPTS[@]}" \
|
|||
|
|
"${EXCLUDES[@]}" \
|
|||
|
|
"${SRC}" "${SNAPSHOT_DIR}/" | tee -a "${LOG_FILE}"
|
|||
|
|
then
|
|||
|
|
log "rsync erfolgreich abgeschlossen."
|
|||
|
|
else
|
|||
|
|
fail "rsync ist mit einem Fehler beendet worden."
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# latest-Symlink aktualisieren
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
ln -sfn "${SNAPSHOT_DIR}" "${LAST_LINK}"
|
|||
|
|
log "latest-Link zeigt jetzt auf ${SNAPSHOT_DIR}"
|
|||
|
|
|
|||
|
|
########################################
|
|||
|
|
# Snapshot-Rotation
|
|||
|
|
########################################
|
|||
|
|
|
|||
|
|
cd "${DEST_BASE}/${HOST}"
|
|||
|
|
|
|||
|
|
# Nur Verzeichnisse mit Datum/Zeit-Präfix (Beginn mit '20') betrachten
|
|||
|
|
SNAPS=(20*/)
|
|||
|
|
|
|||
|
|
if (( ${#SNAPS[@]} > KEEP )); then
|
|||
|
|
log "Rotationslauf: Es existieren ${#SNAPS[@]} Snapshots, KEEP=${KEEP}."
|
|||
|
|
|
|||
|
|
# Sortiert nach Datum (neuestes zuerst), alle älteren als KEEP löschen
|
|||
|
|
ls -1dt 20*/ | tail -n +$((KEEP + 1)) | while read -r old; do
|
|||
|
|
log "Lösche alten Snapshot: ${old}"
|
|||
|
|
rm -rf -- "${old}"
|
|||
|
|
done
|
|||
|
|
else
|
|||
|
|
log "Rotationslauf: Es müssen keine alten Snapshots gelöscht werden (Anzahl: ${#SNAPS[@]}, KEEP=${KEEP})."
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log "Backup abgeschlossen."
|