claim.gif
Linux Magazin Linux User Easy Linux Ubuntu User International Linux Community
Erschienen in EasyLinux 04/2007   »   Ausgabe bestellen

Flexible Backups mit Shell-Befehlen

Manuelle Ablage

Hans-Georg Eßer


Datensicherung gelingt am schnellsten in der Shell, wenn Sie die passenden Kommandos kennen -- dieser Artikel stellt sie vor und präsentiert zudem ein kleines Skript für inkrementelle Backups.

In der Titelstrecke haben Sie ab Seite 24 erfahren, wie Sie mit grafischen Tools (Ark, X Archiver und X Archive Manager) Sicherungen wichtiger Dateien anlegen. Im Hintergrund arbeiten dabei immer tar und eines der beiden Komprimierprogramme gzip und bzip2. Wenn Sie auf eine komfortable Oberfläche verzichten können, nutzen Sie diese Kommandozeilentools deutlich flexibler, etwa für inkrementelle Backups.

Szenario und Vorbereitungen

Wir gehen in diesem Artikel davon aus, dass Sie für das Backup eine zweite Festplatte verwenden -- die kann im Rechner fest eingebaut sein, besser ist es aber, eine externe USB-Platte zu verwenden, die Sie räumlich vom PC getrennt aufbewahren: Das erhöht die Sicherheit.

Beim Anschließen der zweiten Platte wird Linux diese automatisch einbinden. Sie müssen darauf mindestens eine Linux-Partition angelegt haben. Für alle folgenden Schritte benötigen Sie eine Shell mit Root-Rechten. Öffnen Sie also mit [Alt-F2] und Eingabe von konsole ein Terminalfenster und geben Sie dort den Befehl su und anschließend Ihr Root-Passwort ein. (Ubuntu-Anwender geben sudo su und dann das Benutzerpasswort ein.)

  1. Zunächst müssen Sie heraus finden, unter welchen Gerätedateinamen Sie die Partitionen Ihrer zweiten Festplatte erreichen. Das erreichen Sie über den Befehl fdisk -l -- der listet sämtliche Partitionen aller Festplatten auf. Listing 1 zeigt eine beispielhafte Ausgabe von fdisk.
Listing 1: Ausgabe von fdisk
# fdisk -l
Platte /dev/hda: 80.0 GByte, 80026361856 Byte
255 Köpfe, 63 Sektoren/Spuren, 9729 Zylinder
Einheiten = Zylinder von 16065 × 512 = 8225280 Bytes

   Gerät  boot.   Anfang    Ende    Blöcke   Id  System
/dev/hda1             1     131    1052226   82  Linux Swap / Solaris
/dev/hda2           132    9729   77095935    f  W95 Erw. (LBA)
/dev/hda5           132    2742   20972826   83  Linux
/dev/hda6          2743    5353   20972826   83  Linux
/dev/hda7          5354    9139   30411013+  83  Linux
/dev/hda8          9140    9729    4739143+   c  W95 FAT32 (LBA)

Platte /dev/sda: 300.0 GByte, 300090728448 Byte
255 Köpfe, 63 Sektoren/Spuren, 36483 Zylinder
Einheiten = Zylinder von 16065 × 512 = 8225280 Bytes

   Gerät  boot.   Anfang    Ende    Blöcke   Id  System
/dev/sda1   *         1   36483  477806080    7  Linux
  1. Im Beispiel ist auf der zweiten Festplatte (/dev/sda) nur eine einzige Partition vorhanden: /dev/sda1. Wenn Sie diese bereits zu einem früheren Zeitpunkt mit einem Linux-Dateisystem formatiert haben, können Sie den folgenden (dritten) Schritt überspringen.
  1. Die Partition muss ein Linux-Dateisystem erhalten, Standard dafür ist das Ext3-Format. Sie formatieren die Partition mit
    mkfs.ext3 /dev/sda1
    Hier ist wichtig, dass Sie sda1 durch den korrekten Partitionsnamen ersetzen, den Sie in Schritt 1 ermittelt haben, damit Sie keine Daten überschreiben!
  1. Legen Sie nun unter /media ein neues Verzeichnis an, das der Mountpoint für die Backup-Partition wird:
    mkdir /media/backup
  1. Dann tragen Sie die Partition in die Datei /etc/fstab ein, damit Linux sie künftig automatisch einbindet. Wenn es sich um eine USB-Platte handelt, können Sie in der folgenden Zeile die Option defaults durch noauto,user ersetzen, damit das System nicht automatisch versucht, die Platte einzubinden, Sie dies aber (ohne Root-Rechte) nach dem Anschließen nachholen können.
    /dev/sda1  /media/backup  ext3  defaults  0  0
    (Hier ersetzen Sie wieder sda1 durch die korrekte Gerätebezeichnung.) Speichern Sie die Datei und verlassen Sie den Editor.
  1. Mounten Sie nun die Partition mit dem Befehl mount /media/backup.
  1. Auf der Backup-Platte legen Sie nun ein Unterverzeichnis für Ihre Sicherungen an und tragen sich selbst als Eigentümer dieses Verzeichnisses ein:
    mkdir /media/backup/sicherungen
    chown Username /media/backup/sicherungen
    Im chown-Aufruf müssen Sie also Ihren eigenen Benutzernamen eintragen.

Damit sind die Vorbereitungen abgeschlossen.

Datensicherung

Für die Sicherung Ihrer privaten Daten benötigen Sie in diesem Szenario keine Root-Rechte: Lesen dürfen Sie die Dateien in Ihrem Home-Verzeichnis ohnehin, und für das Backup-Verzeichnis haben Sie sich bei der Einrichtung die nötigen Rechte gegeben. Wollen Sie sämtliche Home-Verzeichnisse sichern, müssen Sie anders vorgehen und mit Root-Rechten arbeiten.

Im einfachsten Fall sichern Sie Ihr gesamtes privates Home-Verzeichnis, das den Pfad /home/Benutzername hat. Wechseln Sie mit cd (ohne Argumente) dort hin -- wenn Sie die Shell gerade erst gestartet haben, befinden Sie sich bereits am richtigen Ort.

Der folgende Befehl erzeugt ein komprimiertes Archiv (vergleichbar mit einem zip-Archiv):

tar -c -j -f /media/backup/sicherungen/backup-2007-09-20.tar.bz2 .

Die Optionen, die Sie tar hier übergeben, haben die folgenden Bedeutungen:

Es ist üblich, beim Tippen etwas Zeit zu sparen und alle Optionen zusammenzufassen: Statt tar -c -j -f /media/... schreiben Sie kurz tar -cjf /media/... und benötigen damit vier Tastenanschläge weniger.

Auf diese Weise aufgerufen, erledigt tar seine Arbeit kommentarlos. Wollen Sie beim Erstellen des Archivs beobachten, welche Dateien aus Ihrem Home-Verzeichnis das Programm sichert, geben Sie zusätzlich die Option -v an, die Sie auch in die Optionensammlung integrieren können (tar -cvjf ...):

$ tar -cvjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 .
./
./.qt/
./bin/
./.kde/
./.kde/share/
./.kde/share/apps/
./.kde/share/apps/kconf_update/
./.kde/share/apps/kconf_update/log/
./.kde/share/apps/kconf_update/log/update.log
[...]

Wichtig ist, dass Sie in diesem Befehl nicht den Punkt vergessen, der ganz am Ende hinter dem Archivnamen und einem Leerzeichen folgt: Der Punkt . gibt immer das aktuelle Verzeichnis an, hier also Ihr Home-Verzeichnis.

Welche Dateien das Archiv enthält, können Sie auch nachträglich bequem feststellen: Dazu rufen Sie tar mit den Optionen -tjf und dem Archivdateinamen auf, im Beispiel:

$ tar -tjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2
./
./.qt/
./bin/
./.kde/
[...]

Um bei einem gut gefüllten Archiv nicht den Überblick zu verlieren, leiten Sie die Ausgabe an den Dateibetrachter less weiter, indem Sie hinter den Befehl noch | less schreiben; dann können Sie mit den Cursortasten, [Seite hoch] und [Seite runter] durch die Dateiliste blättern. Sie beenden die Anzeige mit [Q] (für "quit").

Daten zurücksichern

Wenn Sie regelmäßig auf die beschriebene Weise ein Backup erzeugen, verlieren Datenverluste ihren Schrecken. Haben Sie versehentlich eine Datei gelöscht oder sogar durch einen Plattenschaden Ihr ganzes Home-Verzeichnis verloren, spielen Sie einfach die Dateien aus dem Backup wieder ein -- bei Totalausfall der Platte ist dazu erst eine Linux-Neuinstallation nötig.

Wollen Sie nur eine einzelne Datei zurücksichern, suchen Sie diese im Archiv mit Hilfe von grep: Dieses Programm durchforstet Dateien nach Suchbegriffen, aber Sie können es auch via Befehlsverkettung auf die Ausgabe von tar ansetzen. Ist Ihre Backup-Partition wieder unter /media/backup/ eingebunden, sucht der folgende Befehl alle Dateien, deren Dateinamen die Zeichenkette txt enthalten:

$ tar -tjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 | grep txt
./manual.txt
./manual.txt.gz
./.thunderbird/5x893d04.default/signons.txt

Finden Sie die Datei auf diesem Weg nicht, betrachten Sie den ganzen Archivinhalt, wie weiter oben beschrieben, mit tar und less. Sie können auch einfach mit dem Konqueror die Backup-Datei auf der zweiten Platte öffnen und nach den fehlenden Daten suchen.

Um nun aus dem Beispiel die Datei manual.txt zurückzusichern, wechseln Sie mit cd in Ihr Home-Verzeichnis und verwenden die tar-Option -x (extract, auspacken):

tar -xjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 ./manual.txt

Den Dateinamen geben Sie dabei mit vollem Pfad genau so an, wie Sie ihn in der Ausgabe von tar -tjf ... gefunden haben, also mit ./ am Anfang -- anderenfalls beschwert sich tar, dass es die Datei nicht findet:

$ tar -xjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 manual.txt
tar: manual.txt: Nicht im Archiv gefunden.
tar: Fehler beim Beenden, verursacht durch vorhergehende Fehler.
$ tar -xjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 ./manual.txt

Haben Sie ein komplettes Verzeichnis gelöscht, können Sie es in einem Arbeitsgang wiederherstellen -- Verzeichnisse erkennen Sie im Inhaltsverzeichnis des Archivs daran, dass der Name mit einem Schrägstrich endet. Um etwa den ganzen Ordner .gnupg wiederherzustellen, geben Sie ihn wie einen Dateinamen an; verwenden Sie dabei zusätzlich die Option -v, damit tar anzeigt, welche Dateien es aus dem Archiv holt:

$ tar -xvjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 ./.gnupg/
./.gnupg/
./.gnupg/pubring.gpg~
./.gnupg/pubring.gpg
./.gnupg/trustdb.gpg
./.gnupg/random_seed
./.gnupg/gpg.conf
./.gnupg/secring.gpg

Nach einem Totalverlust steht zunächst der Einbau einer neuen Platte samt Neuinstallation eines Linux-Systems an. Danach stellen Sie sicher, dass Sie wieder auf die zweite Platte (mit dem Backup) zugreifen können -- wir nehmen im Folgenden an, dass Sie wieder den Mountpoint /media/backup gewählt haben. Entpacken Sie nun das komplette Archiv. Das sollten Sie unmittelbar nach dem Neuaufsetzen des Rechners erledigen, weil Sie dabei auch neu angelegte Dateien in Ihrem frischen Home-Verzeichnis überschreiben. Der richtige Befehl lautet

$ tar -xvjf /media/backup/sicherungen/backup-2007-09-20.tar.bz2 .
./
./.qt/
./bin/
./.kde/
./.kde/share/
./.kde/share/apps/
[...]

-- wieder mit einem abschließenden Punkt, der aber hier nicht für das aktuelle Arbeitsverzeichnis, sondern für die oberste Verzeichnisebene im Archiv steht.

Inkrementelle Backups

Um nur Daten zu sichern, die sich seit dem letzten Backup verändert haben, erstellen Sie ein kleines Skript, das einen Blick in Ihr Backup-Verzeichnis wirft, dort den Dateinamen und das Datum der letzten Archivdatei feststellt und dann eine neue Backup-Datei erzeugt, die nur solche Dateien enthält, deren Änderungsdatum zeitlich hinter dem Änderungsdatum des alten Backups liegt.

Das Beispiel-Skript inkrementell.sh in Listing 2 erzeugt Backup-Dateien, die backup-001.tgz, backup-002.tgz usw. heißen -- der Archiv-Dateityp ist hier .tgz (eine Kurzschreibweise für .tar.gz), Sie können alternativ auch das .tar.bz2-Format verwenden, das wir in den Beispielen weiter oben benutzt haben.

Listing 2: inkrementell.sh
#!/bin/bash
# EasyLinux-Backup-Skript

# Einstellungen
BACKUPDIR=/media/backup/sicherungen
TIMESTAMP=backup-timestamp.dat
DATA=/home/Benutzername

if [ ! -d "${BACKUPDIR}" ]; then
  echo "Das Backup-Verzeichnis ${BACKUPDIR} existiert nicht."
  exit 1
fi

set -- ${BACKUPDIR}/backup-???.tgz        # Alle Backup-Dateinamen in due Variablen $1, $2 etc. einlesen
lastname=${!#}                            # Letzter Backup-Dateiname steht in ${!#}
backupnr=${lastname##*backup-}            # Dateipfad bis einschließlich "backup-" entfernen
backupnr=${backupnr%%.*}                  # Alles hinter dem ersten Punkt entfernen -- funktioniert auch mit .tar.gz
backupnr=${backupnr//\?/0}                # Falls es noch keine Backups gab, ist backupnr=???, also ? durch 0 ersetzen.
backupnr=$[10#${backupnr}]                # Führende Nullen entfernen (siehe base# in der Bash-Manpage)

if [ "$[backupnr++]" -ge 999 ]; then      # Erhöhen von $backupnr um 1 ist implizit in der Abfrage eingebaut
  echo "Fehler: Schon 999 Backups da!"
  exit 1
fi

backupnr=000${backupnr}                   # Nullen voranstellen
backupnr=${backupnr: -3}                  # Und die letzten 3 Ziffern herausschneiden. Leerzeichen *unbedingt*
                                          # nötig wegen dem Minus, da :- eine andere Shell-String-Operation ist.
filename=backup-${backupnr}.tgz
echo "Sichere veränderte Daten in ${filename}"
tar -czf ${BACKUPDIR}/${filename} -g ${BACKUPDIR}/${TIMESTAMP} ${DATA}

Das Skript sucht zunächst im Backup-Verzeichnis nach bereits vorhandenen Sicherungen und ermittelt dann die Nummer des nächsten inkrementellen Archivs -- sind etwa bereits backup-001.tgz und backup-002.tgz vorhanden, heißt die nächste Archivdatei backup-003.tgz. Um die Folgenummer herauszufinden, benutzt das Skript einige Profifunktionen der Bash. Damit der tar-Aufruf am Ende des Skripts wirklich nur die neuen oder seit der letzten Sicherung geänderten Dateien in das nächste Archiv schreibt, benutzt er einen tar-Trick: Über die Option -g Dateiname legt tar eine so genannte Timestamp-Datei an, die in kodierter Form das Datum der aktuellen Sicherung speichert. Beim zweiten und allen weiteren tar-Aufrufen mit gleichem Argument liest das Tool diese Datei zunächst und erkennt daran, wann die letzte Sicherung stattfand. Beim Durchsuchen des zu archivierenden Verzeichnisses prüft tar dann bei jeder Datei das Datum der letzten Änderung und speichert nur die Dateien, die jünger sind.

Der Kasten Shell-Tipps extrem verrät, wie die Ermittlung der Backup-Dateinamen funktioniert. Anstelle der im Skript verwendeten Nummerierung der Backup-Dateien (001, 002 etc.) können Sie auch Dateinamen aus Datum und Uhrzeit zusammensetzen -- die haben dann ebenfalls die richtige Reihenfolge. Dann fällt der mittlere Teil des Skripts (alles ab dem Aufruf von set bis auf die letzten drei Zeilen) weg, und die Definition des Dateinamens wird zu

filename=backup-$(date +%F-%H:%M).tgz

Der hier integrierte Aufruf des Programms date gibt Datum und Uhrzeit in einer frei wählbaren Formatierung aus; das können Sie auch gleich in einer Shell ausprobieren:

$ date +%F-%H:%M
2007-09-19-11:59

Dabei stehen %F, %H und %M für das Datum (im Format JJJJ-MM-TT), die Stunde und die Minute. Die Dateinamen haben dann also die Form backup-2007-09-19-11:59.tgz.

Shell-Tipps extrem

Einige Befehle aus dem Skript für inkrementelle Backups (Listing 2) sind erklärungsbedürftig.

  • Die ersten drei Befehlszeilen definieren die Variablen BACKUPDIR, TIMESTAMP und DATA -- darin stehen der Name des Backup-Verzeichnisses (auf der zweiten Platte), der Name der Timestamp-Datei, in der sich tar den Zeitpunkt der letzten Sicherung merkt, und der Name des Verzeichnisses, das Sie sichern wollen (hier Ihr ganzes Home-Verzeichnis).
  • Es folgt eine Überprüfung, ob es das Backup-Verzeichnis überhaupt gibt: In der Shell liefert der Test [ -d Verzeichnis ] den Wahrheitswert "wahr" zurück, wenn das Verzeichnis existiert -- das Skript will aber wissen, ob es fehlt und setzt darum noch ein Ausrufezeichen voran ([ ! -d Verzeichnis ]), welches das Ergebnis der Abfrage umkehrt: Wahr wird Falsch, Falsch wird Wahr.
  • Der nächste Block nutzt eine Raffinesse der Shell aus: set -- ... weist alle folgenden Argumente den Variablen $1, $2 usw. zu, die im Normalfall dazu dienen, Kommandozeilenargumente eines Skripts aufzunehmen. Nach diesem Aufruf stehen also alle Backup-Dateinamen in diesen Variablen. Auf die letzte belegte Variable können Sie mit ${!#} zugreifen, das Skript merkt sich den letzten Archivnamen in lastname.
  • Es folgen mehrere Ersetzungen, in denen jeweils backupnr einen neuen Wert erhält -- das Ziel ist, aus einem Namen wie backup-014.tgz die Zahl 14 zu extrahieren. Zunächst schneidet der Befehl backupnr=${lastname##*backup-} alle Zeichen bis zur dreistelligen Zahl ab: Aus /media/backup/sicherungen/backup-014.tgz wird hier also 014.tgz. Dann entfernt der Befehl backupnr=${backupnr%%.*} alle Zeichen hinter dem Punkt, so dass 014 übrig bleibt. Der nächste Aufruf backupnr=${backupnr//\?/0} deckt den Sonderfall auf, dass das Skript zum ersten Mal läuft und es noch gar keine Backup-Datei gibt -- dann stehen in der Variable nun anstelle einer dreistelligen Zahl drei Fragezeichen. Diese ersetzt das Skript durch Nullen. Zuletzt entfernt backupnr=$[10#${backupnr}] führende Nullen, so dass 014 zu 14 wird. Mit 10# teilt dieser Befehl der Shell mit, dass es die folgenden Zeichen als Zahlenwert im (üblichen) Zehnersystem interpretieren soll.
  • Der Rest ist einfacher: Eine Fehlerabfrage verhindert, dass Sie mehr als 999 Archivdateien erstellen, denn die 1000. Datei würde mit dem Namen backup-1000.tgz das Dateinamensformat sprengen. In dieser Abfrage erhöht das Skript automatisch den Wert in backupnr: Das erreicht es über die beiden Plus-Zeichen in $[backupnr++]. Aus dem Beispielwert 14 ist nun 15 geworden.
  • Schließlich entsteht wieder eine dreistellige Zahl. Damit das Skript nicht testen muss, wie viele Ziffern die Zahl schon hat, stellt es einfach vorne drei Nullen voran (backupnr=000${backupnr}) und schneidet anschließend alles bis auf die letzten drei Zeichen ab (${backupnr: -3}) -- so wird aus 14 zunächst 00014 und dann 014.
  • In der drittletzten Zeile nimmt die Variable filename den aus backup-, der Dateinummer und .tgz zusammengesetzten neuen Dateinamen auf, und schließlich erzeugt der tar-Aufruf das neue Archiv.

... und wieder zurück

Um Daten aus den zahlreichen inkrementellen Backup-Dateien zurückzuholen, die Sie mit dem vorgestellten Skript erzeugen, müssen Sie alle Archive betrachten; für eine vollständige Rücksicherung (nach dem kompletten Verlust aller Daten und Neuinstallation des Linux-Systems) können Sie etwa mit einer for-Schleife alle Archive auspacken:

BACKUPDIR=/media/backup/sicherungen
for archiv in ${BACKUPDIR}/backup-*.tgz; do
  tar -xzf $archiv -C /
done

Diese for-Schleife arbeitet die Archive in der richtigen Reihenfolge ab -- dadurch werden Dateien, die wegen Änderungen in mehreren Archiven zu finden sind, auch mehrfach ausgepackt, Sie erhalten durch die Sortierung aber am Ende die korrekte (letzte) Dateiversion. Der Parameter -C / sorgt dafür, dass tar die Dateien relativ zum Wurzelverzeichnis / auspackt, denn in den Archiven stehen die absoluten Pfadnamen (also z. B. /home/benutzer/Dokumente/datei.txt und nicht einfach Dokumente/datei.txt).

Einzelne Dateien oder Verzeichnisse sichern Sie aus der inkrementellen Backup-Sammlung mit dem gleichen Trick zurück, den wir weiter oben für einfache Archive beschrieben haben: Ersetzen Sie zunächst in der Schleife den tar-Parameter -x durch -t, um die Inhalte der Archive anzeigen zu lassen, und hängen Sie dann im nächsten Durchgang dieser Schleife hinter -C / den Datei- oder Verzeichnisnamen so an den tar-Aufruf an, wie Sie ihn im Archiv-Listing gefunden haben (siehe Listing 3). (hge)

Listing 3: Beispiel für das Auspacken
$ BACKUPDIR=/media/backup/sicherungen
$ ls -l $BACKUPDIR/
insgesamt 144
-rw-r--r-- 1 hesser hesser 113011 19. Sep 12:46 backup-001.tgz
-rw-r--r-- 1 hesser hesser  11016 19. Sep 12:48 backup-002.tgz
-rw-r--r-- 1 hesser hesser  10324 19. Sep 12:49 backup-003.tgz
-rw-r--r-- 1 hesser hesser    764 19. Sep 12:49 backup-timestamp.dat
$ for archiv in ${BACKUPDIR}/backup-*.tgz; do
> tar -tzf $archiv -C /  |  grep backup.txt
> done
home/hesser/Dokumente/guru-backup/guru-backup.txt
home/hesser/Dokumente/guru-backup/guru-backup.txt
home/hesser/Dokumente/guru-backup/guru-backup.txt

Die Datei guru-backup.txt erscheint hier dreimal, weil sie in allen drei inkrementellen Archiven enthalten ist. Die folgende Schleife sorgt dafür, dass alle Versionen entpackt werden -- dabei bleibt am Ende nur die letzte (also beste) Sicherung übrig.

$ for archiv in ${BACKUPDIR}/backup-*.tgz; do
> tar -xzf $archiv -C /  home/hesser/Dokumente/guru-backup/guru-backup.txt
> done

Infos
[1] Artikel zu tar, gzip und bzip2, Heike Jurzik: "Komprimierungskünstler", EasyLinux 07/2004, S. 84 f., http://www.easylinux.de/2004/07/084-guru-tar/

Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links" nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedruckten Fassung entsprechen.

Druckerfreundliche Version | Feedback zu dieser Seite | Datenschutz | © 2019 COMPUTEC MEDIA GmbH | Last modified: 2008-11-05 17:26

Nutzungsbasierte Onlinewerbung

[Linux-Magazin] [LinuxUser] [Linux-Community] [Admin-Magazin] [Ubuntu User] [Smart Developer] [Linux Events] [Linux Magazine] [Ubuntu User] [Admin Magazine] [Smart Developer] [Linux Magazine Poland] [Linux Community Poland] [Linux Magazine Brasil] [Linux Magazine Spain] [Linux Technical Review]