Post

Datenbackups, Änderungsprüfung, Wiederherstellen

Datenbackups, Änderungsprüfung, Wiederherstellen

Datenbackups unter Linux

Nachdem mir gerade unverhofft eine SSD ausgestiegen ist, hat das Thema Backups wieder etwas Priorität bekommen. Der Datenverlust hält sich zwar in Grenzen, da bei mir fast alles auf git-Servern oder in der Cloud liegt, aber einige Spielstände sind weg (und auch mein Loot, Mist).

Backuplösungen gibt es viele, daher dokumentiere ich hier mal meine Praxiserfahrungen mit den Tools.

Mein Backupziel ist aktuell eine externe SSD, die ich eventuell später mal an mein Home-Mini-Server anstecke und dann via SSH bespiele.

Als Software starte ich mit duplicity und dem Gnome FrontEnd Deja Dup.

Einrichten Backup

Total einfach, Quellverzeichnisse auswählen, Ausschlüsse definieren, Zielverzeichnis festlegen und Verschlüsselung (Optional) und los geht’s.

Änderungen sichten

Wenn ich eine Backup zurückspielen will, dann muss ich erstmal wissen, was sich geändert hat. Nicht immer will ich einen Komplett-Restore machen, sondern manchmal nur Zeugs, was sich seit Tag X geändert hat.

Mit Duplicity kann man sich eine Liste aller Dateien im Backup (Achtung, kann lang werden!) ausgeben lassen:

1
2
3
4
5
# Syntax: duplicity list-current-files  [url]  > <Dateiliste>
# url ist file:// oder ssh:// , siehe Duplicity man page

# Beispiel für das Erstellen der List-Datei
duplicity list-current-files file:///home/ghorwin/BackupTest/ > files.txt

Bei Verwendung von file:// muss ein absoluter Pfad verwendet werden.

Man erhält eine Datei in folgendem Format:

1
2
3
4
5
6
7
8
9
10
11
12
Last full backup date: Thu Jun 12 16:41:45 2025
Thu Dec 26 20:36:45 2024 .
Thu Dec 26 18:24:34 2024 home
Thu Jun 12 16:45:34 2025 home/ghorwin
Thu Jun 12 16:45:54 2025 home/ghorwin/.cache
Thu Jun 12 16:46:52 2025 home/ghorwin/.cache/deja-dup
Thu Jun 12 16:46:48 2025 home/ghorwin/.cache/deja-dup/metadata
Thu Jun 12 16:46:48 2025 home/ghorwin/.cache/deja-dup/metadata/README
Sun Mar 30 20:00:44 2025 home/ghorwin/git
Wed Jun 11 07:51:34 2025 home/ghorwin/git/_webpages
Tue Jun 10 17:44:12 2025 home/ghorwin/git/_webpages/schneggenport
Thu May  8 07:22:37 2025 home/ghorwin/git/_webpages/schneggenport/.devcontainer

Die kann man nun nach einem bestimmten Datum filtern, dazu kann man folgendes Python-Script verwenden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import re
import argparse
from datetime import datetime

# Regex zum Erkennen des Zeitstempels
timestamp_pattern = re.compile(
    r'^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) '
    r'(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) '
    r'\d{2} \d{2}:\d{2}:\d{2} \d{4}'
)

# Funktion zum Parsen des Zeitstempels am Anfang einer Zeile
def parse_line_timestamp(line):
    try:
        return datetime.strptime(line[:24], "%a %b %d %H:%M:%S %Y")
    except ValueError:
        return None

# Filtert Zeilen zwischen start_time und end_time
def filter_lines(input_file, output_file, start_time, end_time):
    with open(input_file, 'r') as infile:
        for line in infile:
            ts = parse_line_timestamp(line)
            if ts and start_time <= ts <= end_time:
                print(line.strip())

# Kommandozeilenargumente mit argparse parsen
def parse_args():
    parser = argparse.ArgumentParser(description="Filtert Zeilen aus einer Datei basierend auf einem Zeitstempelbereich.")
    parser.add_argument("filepath", help="Pfad zur Eingabedatei")
    parser.add_argument("start", help="Startzeitpunkt (Format: 'YYYY-MM-DD HH:MM:SS')")
    parser.add_argument("end", nargs="?", default=None, help="Optionaler Endzeitpunkt (Format: 'YYYY-MM-DD HH:MM:SS')")

    args = parser.parse_args()

    try:
        start_time = datetime.strptime(args.start, "%Y-%m-%d %H:%M:%S")
    except ValueError:
        parser.error("Ungültiger Startzeitpunkt. Verwende Format: 'YYYY-MM-DD HH:MM:SS'")

    if args.end:
        try:
            end_time = datetime.strptime(args.end, "%Y-%m-%d %H:%M:%S")
        except ValueError:
            parser.error("Ungültiger Endzeitpunkt. Verwende Format: 'YYYY-MM-DD HH:MM:SS'")
    else:
        end_time = datetime.now()

    return args.filepath, start_time, end_time

if __name__ == "__main__":
    filepath, start_time, end_time = parse_args()
    output_path = "gefilterte_zeilen.txt"
    filter_lines(filepath, output_path, start_time, end_time)

Script hat ChatGPT generiert, zwar mit einem sehr detaillierten, aber immerhin auf Deutsch formuliertem Prompt.

This post is licensed under CC BY 4.0 by the author.