From d52807f19b4e6a1c8a34ad1118659f25818fec30 Mon Sep 17 00:00:00 2001 From: alf Date: Mon, 28 Sep 2026 14:06:00 +0000 Subject: [PATCH] =?UTF-8?q?konsolidieren.sh=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- konsolidieren.sh | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 konsolidieren.sh diff --git a/konsolidieren.sh b/konsolidieren.sh new file mode 100644 index 0000000..407d75a --- /dev/null +++ b/konsolidieren.sh @@ -0,0 +1,146 @@ +#!/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" + [ -n "$LOG_FILE" ] && echo "$msg" >> "$LOG_FILE" +} + +usage() { echo "Usage: $0 [-n] " >&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)" + +declare -A MONTH_FOLDERS=() +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 + MONTH_FOLDERS["$month"]+="$d"$'\n' +done + +# pro Monat: aelteste zuerst mergen, neueste gewinnt +for month in "${!MONTH_FOLDERS[@]}"; do + mapfile -t sources < <(sort <<< "${MONTH_FOLDERS[$month]}" | grep -v '^$') + dest="$ARCHIVE/$month" + 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.)" \ No newline at end of file