Ein Projekt-Backup mit zip erstellen, prüfen und wiederherstellen
Damit ein ZIP-Backup den aktuellen Projektstand abbildet, erstellen Sie bei jedem Lauf ein neues
Archiv. Wenn Sie zip -r auf ein vorhandenes Archiv anwenden, werden Einträge
hinzugefügt oder aktualisiert. Dateien, die Sie aus der Quelle gelöscht haben, können jedoch
enthalten bleiben. Das Info-ZIP-Handbuch
dokumentiert dieses Verhalten. Das folgende Bash-Skript erstellt ein neues Archiv, prüft es und
ersetzt erst danach das vorherige Backup.
Diese Anleitung richtet sich an Linux mit Bash, Info-ZIP Zip 3.0, UnZip 6.0, GNU coreutils,
findutils und diffutils. Prüfen Sie die Info-ZIP-Versionen mit zip -v und
unzip -v. Das Skript verwendet GNU mv -T und muss daher
für macOS angepasst werden. Make ist optional.
Stoppen Sie vor dem Archivieren Builds und andere Prozesse, die in das Projekt schreiben.
Das Ergebnis bildet ein Verzeichnis ab, das während des Laufs unverändert bleibt;
zip erstellt keinen Dateisystem-Snapshot. Verwenden Sie gewöhnliche
Dateien, Verzeichnisse und symbolische Links ohne Steuerzeichen in ihren Namen.
Ein kleines Projekt vorbereiten
Führen Sie diesen Block in Bash aus. Er erstellt ein temporäres Testprojekt und verweigert die
Wiederverwendung eines vorhandenen Verzeichnisses namens zip-demo.
Durch die Klammern bleibt Ihre Shell in ihrem ursprünglichen Verzeichnis.
(
set -euo pipefail
mkdir zip-demo
cd zip-demo
mkdir -p project_directory/src project_directory/node_modules project_directory/dist
printf 'version one\n' > project_directory/src/app.txt
printf 'project notes\n' > 'project_directory/read me.txt'
printf 'dependency\n' > project_directory/node_modules/omit.txt
printf 'generated\n' > project_directory/dist/omit.txt
)
Bei jedem Lauf ein neues Archiv erstellen
Speichern Sie dieses Skript als zip-demo/backup.sh. Es archiviert
project_directory relativ zu seinem Arbeitsverzeichnis. Bewahren Sie das Skript,
das temporäre Archiv und das endgültige Archiv neben dem Projekt außerhalb des Quellbaums auf.
#!/usr/bin/env bash
set -euo pipefail
umask 077
unset ZIPOPT UNZIP UNZIPOPT
source_dir=project_directory
backup_date=$(date +%Y-%m-%d)
archive_name="project_backup_$backup_date.zip"
if [[ ! -d "$source_dir" || -L "$source_dir" ]]; then
printf 'Source must be a real directory: %s\n' "$source_dir" >&2
exit 1
fi
if [[ -L "$archive_name" || ( -e "$archive_name" && ! -f "$archive_name" ) ]]; then
printf 'Destination must be a regular file or absent: %s\n' "$archive_name" >&2
exit 1
fi
find "$source_dir" -type d -print > /dev/null
work_dir=$(mktemp -d './.zip-backup.XXXXXXXX')
trap 'rm -rf -- "$work_dir"' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
zip -q -6 -r -MM -y "$work_dir/archive.zip" "$source_dir" \
-x '*/.git' '*/.git/*' \
'*/node_modules' '*/node_modules/*' \
'*/dist' '*/dist/*' \
'*.tmp' '*.tmp/*' '*.temp' '*.temp/*' \
'*/.DS_Store' '*/.DS_Store/*'
unzip -tq "$work_dir/archive.zip"
mv -fT -- "$work_dir/archive.zip" "$archive_name"
printf 'Backup created: %s\n' "$archive_name"
Führen Sie es in dem Verzeichnis aus, in dem Sie zip-demo erstellt haben:
(cd zip-demo && bash backup.sh)
Bei Erfolg nennt die letzte Zeile project_backup_YYYY-MM-DD.zip mit dem lokalen Datum des Rechners.
Ein erfolgreicher Lauf am selben Tag ersetzt diese Datei ohne Rückfrage. Hinzugefügte Dateien
erscheinen, geänderte Dateien werden erneut gelesen und gelöschte Dateien verschwinden. Das gilt
auch, wenn eine Änderung die Größe und den Änderungszeitpunkt einer Datei unverändert lässt.
Bei unveränderten Eingaben bleiben die wiederhergestellten Inhalte gleich; byte-identische
Archive werden nicht zugesichert.
Das Ziel wird erst ersetzt, wenn Komprimierung und Prüfung erfolgreich sind. Eine fehlende Quelle, eine nicht lesbare Datei oder eine fehlgeschlagene Prüfung liefert einen Status ungleich null, ohne Erfolgsmeldung. Das vorherige Backup bleibt erhalten. Ein Verzeichnis oder ein symbolischer Link als Ziel wird abgelehnt. Führen Sie jeweils nur ein Backup in einem Verzeichnis aus, das Sie kontrollieren.
Der anfängliche Aufruf von find prüft, ob der gesamte Quellbaum durchlaufen
werden kann, einschließlich ausgeschlossener Verzeichnisse. Das ist wichtig, weil
zip ein nicht lesbares Verzeichnis selbst mit
-MM stillschweigend überspringen kann. Die Optionen haben konkrete
Aufgaben: -r durchläuft das Verzeichnis,
-MM führt bei fehlenden oder nicht lesbaren Eingabedateien zum Abbruch,
und -y speichert symbolische Links statt ihrer Ziele. Durch die
Anführungszeichen um die Ausschlussmuster übernimmt zip den
Musterabgleich. Dies sind Info-ZIP-Optionen.
Das Skript entfernt geerbte ZIP/UnZip-Optionen, damit Shell-Voreinstellungen sein Verhalten nicht
unbemerkt ändern können. GNU mv -fT
ersetzt die Zieldatei und behandelt das Ziel als Dateipfad, nicht als Verzeichnis, in das die Datei
verschoben werden soll.
Ausschlüsse wählen
Das Beispiel lässt .git, node_modules und
dist auf jeder Verzeichnisebene aus, ebenso Namen mit den Endungen
.tmp oder .temp sowie
.DS_Store. Der Musterabgleich erfasst auch Verzeichnisse mit diesen Namen und
deren Inhalte. Zum Beispiel erfasst */node_modules/* sowohl
project_directory/node_modules/omit.txt als auch die Abhängigkeiten eines verschachtelten Pakets.
Diese Ausschlüsse eignen sich für die Übergabe von Quellcode, dessen Abhängigkeiten und Build-Ergebnisse sich erneut erzeugen lassen. Entfernen Sie Ausschlüsse, wenn Sie diese Dateien wiederherstellen müssen. Dotfiles werden ansonsten eingeschlossen. Prüfen Sie das Archiv daher vor der Weitergabe: Diese Liste erkennt keine sensiblen Zugangsdaten. Wenn Sie die Liste ändern, passen Sie auch die Ausschlüsse im nachfolgenden Vergleich an.
Dasselbe Skript mit Make ausführen
Optional können Sie dies als zip-demo/Makefile speichern, mit einem Tabulator vor
bash. Führen Sie make -C zip-demo archive im übergeordneten
Verzeichnis aus. Das Phony-Target wird jedes Mal ausgeführt, und Make meldet ein fehlgeschlagenes
Skript als fehlgeschlagenes Target.
.PHONY: archive
archive:
bash backup.sh
Dateien wiederherstellen und vergleichen
Führen Sie diesen Block am selben Tag wie das Backup im übergeordneten Verzeichnis von
zip-demo aus. Ersetzen Sie bei einem älteren Backup den Datumsausdruck durch
den tatsächlichen Dateinamen. Stellen Sie nur ein Archiv wieder her, dem Sie vertrauen.
(
set -euo pipefail
cd zip-demo
unset UNZIP UNZIPOPT ZIPINFO ZIPINFOOPT
archive_name="project_backup_$(date +%Y-%m-%d).zip"
unzip -tq "$archive_name"
unzip -Z1 "$archive_name"
mkdir restored
unzip -q "$archive_name" -d restored
diff -r --no-dereference \
-x .git -x node_modules -x dist -x '*.tmp' -x '*.temp' -x .DS_Store \
project_directory restored/project_directory
)
Die Liste der Archiveinträge sollte src/app.txt und
read me.txt unter project_directory/ enthalten, ohne Abhängigkeiten
und generierte Dateien. Der wiederhergestellte Verzeichnisbaum behält dieses oberste Verzeichnis
bei. diff erzeugt keine Ausgabe und endet mit Status null, wenn die
enthaltenen Dateiinhalte, die Verzeichnisstruktur und die Ziele symbolischer Links übereinstimmen.
Eigentümer, Berechtigungen und Zeitstempel werden nicht verglichen.
mkdir restored schlägt gezielt fehl, wenn dieses Ziel bereits existiert, bevor das
Entpacken es verändert. Wählen Sie für eine erneute Wiederherstellung sowohl im Befehl zum
Entpacken als auch im Vergleichsbefehl einen neuen Verzeichnisnamen. Wenn das Entpacken teilweise
fehlschlägt, lassen Sie das unvollständige Verzeichnis separat bestehen und versuchen Sie es erneut
mit einem neuen Verzeichnis.
Das UnZip-Handbuch erklärt, dass
-t Einträge dekomprimiert und ihre gespeicherten CRCs prüft. Das erkennt
beschädigte Daten, belegt aber nicht, dass Sie die richtigen Dateien eingeschlossen haben.
Die separate Wiederherstellung mit Vergleich prüft diesen Aspekt, sofern die Quelle seit dem
Backup unverändert geblieben ist.
Eine Kompressionsstufe wählen
Ändern Sie -6 im Skript, um Stufen zu vergleichen:
| Option | Zweck |
|---|---|
-0 | Dateien ohne Komprimierung speichern |
-1 | Kompressionsgeschwindigkeit bevorzugen |
-6 | Die Standard-Kompressionsstufe von Info-ZIP verwenden |
-9 | Mehr Zeit für den Versuch aufwenden, stärker zu komprimieren |
Die Optionen zur Kompressionsstufe steuern den Kompressionsaufwand. Es gibt keine feste prozentuale Einsparung, und eine höhere Stufe muss kein nennenswert kleineres Archiv erzeugen. Vergleichen Sie Laufzeit und Archivgröße an Ihrem tatsächlichen Projekt und stellen Sie anschließend jeden Kandidaten wieder her. Beachten Sie, dass dieses Skript die Ausgabe desselben Tages ersetzt. Speichern Sie daher jeden Kandidaten separat, wenn Sie ihn behalten möchten.
Bei Bedarf separat verschlüsseln
Dieses Archiv ist unverschlüsselt. Die Option zip -e von Info-ZIP verwendet
eine schwache traditionelle ZIP-Verschlüsselung; die
Info-ZIP-FAQ warnt vor deren Einschränkungen.
Wählen Sie für sensible Inhalte einen separaten Verschlüsselungsworkflow, etwa mit
age, das Empfängerschlüssel und Passphrasen unterstützt. Stellen
Sie sicher, dass der Empfänger das Archiv entschlüsseln und wiederherstellen kann, bevor Sie sich
auf diesen Workflow verlassen. Auch nach der Verschlüsselung bleibt die ursprüngliche,
unverschlüsselte ZIP-Datei auf dem Datenträger, sofern Sie diese Kopie nicht separat verwalten.
Mit Fehlern und Grenzen umgehen
Wenn ein Lauf fehlschlägt, prüfen Sie seine Diagnosemeldung und seinen Exit-Status. Eine ältere ZIP-Datei am Ziel belegt nicht, dass der heutige Versuch erfolgreich war. Halten Sie genügend freien Speicherplatz vor, damit das vorherige und das neue Archiv gleichzeitig Platz finden. Bei einem gewöhnlichen Fehler entfernt die Exit-Trap das temporäre Verzeichnis; bei erzwungenem Abbruch oder Stromausfall kann es zurückbleiben.
Dieses Beispiel prüft Dateiinhalte und Pfade eines Projekts unter Linux. Es bietet kein vollständiges System-Backup-Format für ACLs, erweiterte Attribute, spezielle Dateien oder Hardlink-Beziehungen. Große Archive erfordern möglicherweise Zip64-Unterstützung im Entpackprogramm des Empfängers. Bewahren Sie ältere erfolgreiche Backups entsprechend Ihren eigenen Aufbewahrungsanforderungen auf und testen Sie die Wiederherstellung mit den Werkzeugen, die tatsächlich dafür eingesetzt werden.
