Files
rclone/konsolidieren.sh
2026-09-29 07:31:38 +00:00

157 lines
4.8 KiB
Bash

#!/usr/bin/env bash
#
# konsolidierung.sh
#
# Konsolidiert die Archiv-Ordner eines rsync/rclone-Archivs:
#
# Struktur im Archiv: JJJJ-MM-TT-HH/ (z. B. 2026-08-03-14)
#
# 1. Alle Tages-Ordner eines *abgelaufenen* Monats werden zu einem
# Monatsordner JJJJ-MM zusammengefasst. Dabei gewinnt immer die
# aktuellste Version einer Datei (der spaetere Lauf ueberschreibt
# den frueheren). Der aktuelle Monat bleibt unangetastet.
# 2. Monatsordner, die aelter als RETAIN_MONTHS Monate sind, werden
# zu einem Jahresordner JJJJ zusammengefasst (gleiche Regel:
# neuere Version gewinnt).
#
# Nutzung:
# ./konsolidierung.sh [-n] /pfad/zum/archiv
# -n = Dry-Run (zeigt nur, was passieren wuerde)
#
set -euo pipefail
DRY_RUN=0
RETAIN_MONTHS=3 # Monatsordner juenger als x Monate bleiben Monatsordner
LOG_FILE="" # optional: z. B. LOG_FILE=archiv.log setzen
# Log-Ausgabe: mit Zeitstempel auf Terminal und (falls gesetzt) ins Logfile.
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
echo "$msg"
if [ -n "$LOG_FILE" ]; then
echo "$msg" >> "$LOG_FILE"
fi
return 0
}
usage() { echo "Usage: $0 [-n] <archiv-verzeichnis>" >&2; exit 1; }
while getopts "n" opt; do
case "$opt" in
n) DRY_RUN=1 ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
ARCHIVE="${1:-}"
[ -d "$ARCHIVE" ] || { echo "Fehler: Archiv-Verzeichnis '$ARCHIVE' nicht gefunden." >&2; usage; }
ARCHIVE="$(cd "$ARCHIVE" && pwd)"
# ---- Hilfsfunktionen -------------------------------------------------------
# Letzter abgeschlossener Monat als JJJJ-MM.
last_closed_month() {
date -d "$(date +%Y-%m-01) -1 day" +%Y-%m
}
# Monatsdifferenz zwischen zwei Werten JJJJ-MM (a - b), negativ wenn a frueher.
month_diff() {
local a="$1" b="$2"
local ay am by bm
ay=${a%%-*}; am=${a##*-}
by=${b%%-*}; bm=${b##*-}
echo $(( (10#$ay * 12 + 10#$am) - (10#$by * 12 + 10#$bm) ))
}
# Fuehrt alle Quellordner (aufsteigend sortiert, aeltester zuerst) nach DEST
# zusammen. Spaetere Laeufe ueberschreiben fruehere => aktuellste Version bleibt.
merge_into() {
local dest="$1"; shift
local src
for src in "$@"; do
if [ "$DRY_RUN" -eq 1 ]; then
log "merge: $src -> $dest (loesche danach: $src)"
else
log "merge: $src -> $dest"
mkdir -p "$dest"
cp -a "$src/." "$dest/"
rm -rf "$src"
fi
done
}
# ---- 1. Tagesordner (JJJJ-MM-TT-HH) zu Monatsordnern (JJJJ-MM) ------------
TODAY_MONTH="$(date +%Y-%m)"
CLOSED="$(last_closed_month)"
# abgeschlossene Monate ermitteln, deren Tagesordner existieren
declare -A SEEN=()
for d in "$ARCHIVE"/*; do
[ -d "$d" ] || continue
base="$(basename "$d")"
# exakt JJJJ-MM-TT-HH (4-2-2-2 Ziffern)
[[ "$base" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}$ ]] || continue
month="${base:0:7}"
if [[ "$month" > "$CLOSED" ]]; then
continue # aktueller Monat / Zukunft: nicht anfassen
fi
SEEN["$month"]=1
done
# pro Monat: aelteste zuerst mergen, neueste gewinnt
for month in "${!SEEN[@]}"; do
dest="$ARCHIVE/$month"
# Tagesordner dieses Monats per Glob einsammeln (Glob liefert sie sortiert)
sources=()
for d in "$ARCHIVE/$month"-*; do
[ -d "$d" ] || continue
[[ "$(basename "$d")" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}$ ]] || continue
sources+=("$d")
done
[ ${#sources[@]} -gt 0 ] || continue
if [ -e "$dest" ]; then
# Monatsordner existiert schon (Rest vom letzten Lauf?) -> einfach dazumergen
merge_into "$dest" "${sources[@]}"
else
# ersten (aeltesten) Ordner umbenennen, Rest dazumergen
first="${sources[0]}"
if [ "$DRY_RUN" -eq 1 ]; then
log "mv: $first -> $dest"
merge_into "$dest" "${sources[@]:1}"
else
log "mv: $first -> $dest (Grundordner fuer $month)"
mv "$first" "$dest"
merge_into "$dest" "${sources[@]:1}"
fi
fi
done
# ---- 2. Alte Monatsordner (JJJJ-MM) zu Jahresordnern (JJJJ) ---------------
YEAR_FOLDERS=()
for d in "$ARCHIVE"/*; do
[ -d "$d" ] || continue
base="$(basename "$d")"
[[ "$base" =~ ^[0-9]{4}-[0-9]{2}$ ]] || continue
diff=$(month_diff "${base:0:7}" "$TODAY_MONTH")
if [ "$diff" -ge 0 ] || [ $(( -diff )) -lt "$RETAIN_MONTHS" ]; then
continue # juenger als RETAIN_MONTHS: als Monatsordner stehen lassen
fi
YEAR_FOLDERS+=("$d")
done
if [ ${#YEAR_FOLDERS[@]} -gt 0 ]; then
# aelteste zuerst, damit neuere Monatsversionen ueberschreiben
mapfile -t YEAR_FOLDERS < <(printf '%s\n' "${YEAR_FOLDERS[@]}" | sort)
current_year=""
for d in "${YEAR_FOLDERS[@]}"; do
yr="$(basename "$d" | cut -c1-4)"
dest="$ARCHIVE/$yr"
merge_into "$dest" "$d"
done
fi
echo "Fertig."
[ "$DRY_RUN" -eq 1 ] && echo "(Dry-Run: nichts wurde veraendert.)"