ZIP-Dateien mit unzip sichten und extrahieren
Mit unzip -l sichten Sie eine ZIP-Datei, mit unzip -t prüfen Sie ihre komprimierten Daten,
und mit unzip -n extrahieren Sie sie, ohne vorhandene Dateien zu ersetzen.
Für ein Verzeichnis mit heruntergeladenen Paketen benötigen Sie außerdem ein Skript zur
Stapelverarbeitung, das Fehler auch dann meldet, wenn ein späteres Archiv erfolgreich verarbeitet wird.
Diese Anleitung bietet Ihnen ein kleines Übungsarchiv und ein Skript, das jedes Paket in einem eigenen
Ausgabeverzeichnis ablegt.
Werkzeuge prüfen
Verwenden Sie Linux mit Bash, Info-ZIP UnZip und den üblichen Werkzeugen
cp, mkdir, mktemp und rm.
Zum Erstellen des Übungsarchivs benötigen Sie auch Info-ZIP Zip. Diese Beispiele wurden mit Bash
5.3.15, UnZip 6.00 und Zip 3.0 getestet; verwenden Sie die gepflegten Pakete Ihrer Distribution.
Prüfen Sie die Implementierungen mit:
bash --version && unzip -v && zip -v
Das unten verlinkte Handbuch gilt für Info-ZIP. Ein anderes Programm namens
unzip kann sich daher anders verhalten.
unzip -v zeigt auch UNZIP und UNZIPOPT an.
Diese Umgebungsvariablen können Standardoptionen hinzufügen. Die einzelnen Befehle unten setzen
voraus, dass diese Variablen nicht gesetzt sind; das Skript zur Stapelverarbeitung entfernt sie in
seinem eigenen Prozess.
Arbeiten Sie mit vertrauenswürdigen, unverschlüsselten ZIP-Dateien, etwa mit Paketen eines Anbieters,
den Sie überprüft haben. -t prüft die im Archiv gespeicherten CRC-Werte;
es authentifiziert weder den Anbieter noch stellt es fest, ob die Inhalte sicher sind.
Ein Extraktionsverzeichnis ist keine Sandbox für Benutzer-Uploads. Nicht vertrauenswürdige Archive
benötigen einen isolierten Worker und Ressourcenlimits, die über diesen Workflow hinausgehen.
Eine ZIP-Datei vor dem Extrahieren sichten
Fügen Sie Folgendes in Bash ein, um unzip-demo unterhalb Ihres aktuellen
Verzeichnisses zu erstellen. Falls dieser Name bereits existiert, stoppt die Einrichtung, ohne die
Inhalte zu ändern. Die Klammern sorgen dafür, dass Ihre Shell im ursprünglichen Verzeichnis bleibt.
(
mkdir -- unzip-demo &&
cd -- unzip-demo &&
mkdir -- downloads &&
printf '{"mode":"development"}\n' > config.json &&
printf 'build complete\n' > build.log &&
zip -q ./downloads/package.zip config.json build.log
)
Listen Sie vom selben Ausgangsverzeichnis aus die Einträge auf und prüfen Sie anschließend ihre Daten:
unzip -l ./unzip-demo/downloads/package.zip &&
unzip -t ./unzip-demo/downloads/package.zip
Die Liste enthält config.json und build.log;
der Test sollte keine Fehler melden. Laut Info-ZIP-Handbuch
liest -l die Liste, während -t die Einträge
im Arbeitsspeicher dekomprimiert und ihre CRC-Werte vergleicht. Eine erfolgreiche Auflistung allein
beweist nicht, dass sich die Daten extrahieren lassen.
Extrahieren Sie beide Einträge in ein separates Verzeichnis:
unzip -n ./unzip-demo/downloads/package.zip -d ./unzip-demo/extracted
Nun sollten unzip-demo/extracted/config.json und unzip-demo/extracted/build.log vorhanden sein.
-n überspringt eine vorhandene Datei ohne Rückfrage. Bei einer erneuten
Ausführung bleibt eine lokal bearbeitete Datei config.json daher unverändert,
selbst wenn der Befehl null zurückgibt. Verwenden Sie -o nur, wenn Sie
vorhandene Dateien ersetzen möchten; diese Option unterdrückt Rückfragen zum Überschreiben, indem sie
das Ersetzen erlaubt.
Nur benötigte Inhalte extrahieren
Übergeben Sie ein Muster für Archiveinträge in Anführungszeichen, um JSON-Dateien auszuwählen:
unzip -n ./unzip-demo/downloads/package.zip '*.json' -d ./unzip-demo/config-only
Dies erstellt config-only/config.json, ohne build.log zu extrahieren,
und behält vorhandene Dateien bei erneuten Ausführungen bei. Die Anführungszeichen verhindern, dass
Bash *.json anhand Ihres Arbeitsverzeichnisses expandiert; UnZip wendet
das Muster auf die Archiveinträge an. Zum Ausschließen lässt -x '*.log'
passende Protokolleinträge aus. Um nur die Konfiguration zu sichten, ohne eine Datei zu erstellen,
verwenden Sie unzip -p ./unzip-demo/downloads/package.zip config.json.
Ein Verzeichnis verarbeiten, ohne Fehler zu verbergen
Verwenden Sie für die Stapelverarbeitung eine strengere Ausgaberichtlinie: Ein bereits vorhandenes
Zielverzeichnis für ein Archiv gilt als Fehler, nicht als Aufforderung, Dateien zusammenzuführen.
Speichern Sie Folgendes als extract-zips.sh in Ihrem Ausgangsverzeichnis und führen
Sie es dann wie unten gezeigt mit bash aus. Binden Sie es nicht per
source in Ihre Shell ein.
Das Skript verarbeitet nicht versteckte reguläre Dateien auf der obersten Verzeichnisebene mit der
kleingeschriebenen Endung .zip; symbolische Links und Verzeichnisse werden
übersprungen. Lassen Sie die Eingabedateien während der Ausführung unverändert und verwenden Sie das
Ausgabestammverzeichnis jeweils nur für einen Aufruf gleichzeitig. Es benötigt Platz für die
extrahierten Dateien und eine temporäre Kopie der größten ZIP-Datei.
#!/usr/bin/env bash
if (( $# != 2 )) || [[ -z $1 || -z $2 ]]; then
printf 'Usage: bash extract-zips.sh INPUT_DIR OUTPUT_DIR\n' >&2
exit 2
fi
unset UNZIP UNZIPOPT
input_dir=$1
output_dir=$2
[[ $input_dir = /* ]] || input_dir="$PWD/$input_dir"
[[ $output_dir = /* ]] || output_dir="$PWD/$output_dir"
cd -P -- "$input_dir" || exit 1
input_dir=$PWD
mkdir -p -- "$output_dir" || exit 1
cd -P -- "$output_dir" || exit 1
output_dir=$PWD
scratch=$(mktemp -d -- "$output_dir/.unzip.XXXXXXXX") || exit 1
trap 'rm -rf -- "$scratch"' EXIT
shopt -s nullglob
status=0
count=0
for archive in "$input_dir"/*.zip; do
[[ -f "$archive" && ! -L "$archive" ]] || continue
count=$((count + 1))
name=${archive##*/}
destination="$output_dir/${name%.zip}"
if [[ -e "$destination" || -L "$destination" ]]; then
printf 'Destination already exists: %s\n' "$destination" >&2
status=1
continue
fi
printf 'Checking: %s\n' "$archive"
# UnZip expands archive-name wildcards even inside a quoted shell argument.
if ! cp -f -- "$archive" "$scratch/input.zip" ||
! unzip -tq -P '' "$scratch/input.zip" </dev/null; then
printf 'Cannot copy or validate: %s\n' "$archive" >&2
status=1
continue
fi
if ! mkdir -- "$destination"; then
status=1
continue
fi
if unzip -qo -P '' "$scratch/input.zip" -d "$destination" </dev/null; then
printf 'Extracted: %s\n' "$destination"
else
printf 'Extraction failed; inspect partial output: %s\n' "$destination" >&2
status=1
fi
done
if (( count == 0 )); then
printf 'No regular .zip files found in: %s\n' "$input_dir" >&2
status=1
fi
exit "$status"
Führen Sie das gespeicherte Skript für das Übungsarchiv aus:
bash ./extract-zips.sh ./unzip-demo/downloads ./unzip-demo/batch-output
Eine erfolgreiche erste Ausführung gibt eine Zeile mit Extracted: aus und erstellt
batch-output/package/config.json und batch-output/package/build.log.
Eine zweite Ausführung gibt 1 zurück und lässt dieses Verzeichnis
unverändert. Wählen Sie ein neues Ausgabestammverzeichnis, wenn Sie erneut frisch extrahieren möchten.
Die temporäre Kopie gibt UnZip den wörtlichen Namen input.zip.
Das ist bei Namen wie release[1].zip wichtig: Anführungszeichen in der Shell allein
deaktivieren nicht die eigene Platzhaltererkennung von UnZip für Archivnamen. Der Quellname bestimmt
weiterhin das Ziel. Pfade werden einmal mit cd -P aufgelöst und aus
$PWD übernommen. So verwenden Suche und Extraktion dasselbe physische
Verzeichnis, ohne abschließende Zeilenumbrüche in Verzeichnisnamen zu verlieren.
Jede ZIP-Datei muss -t bestehen, bevor ihr Zielverzeichnis erstellt wird.
-o gilt dann nur innerhalb dieses neu erstellten Verzeichnisses, und
-P '' übergibt ein leeres Passwort, sodass keine interaktive Passwortabfrage
erfolgt. Passwortgeschützte Pakete benötigen einen separaten Workflow. Das Skript setzt die
Verarbeitung nach einem Fehler fort, setzt status aber nie auf null zurück.
Die Subshell- und Prozessregeln von Bash
sorgen dafür, dass Verzeichniswechsel und Optionen des gespeicherten Skripts die aufrufende Shell
nicht beeinflussen.
Auf das Ergebnis reagieren
Das Skript zur Stapelverarbeitung gibt 0 nur zurück, wenn es mindestens
ein geeignetes Archiv gefunden hat und jedes Archiv ohne Warnung oder Fehler verarbeitet wurde.
Bei einem vorhandenen Ziel, fehlenden geeigneten Eingaben oder einem Fehler beim Kopieren, Validieren
oder Extrahieren gibt es 1 zurück;
2 bedeutet, dass die Argumente falsch sind. Ein späterer Erfolg kann
einen früheren Fehler nicht verbergen.
Bewahren Sie die Diagnosemeldungen von UnZip auf, wenn Sie einen Fehler untersuchen. Ein UnZip-Status ungleich null kann auf beschädigte Daten, nicht unterstützte Komprimierung oder Verschlüsselung oder ein Dateisystemproblem hinweisen. All dies als „Datenbeschädigung“ zu bezeichnen, würde Ihre Fehlersuche in die falsche Richtung lenken.
Bei einer fehlgeschlagenen Datenprüfung wird kein Zielverzeichnis für das Archiv erstellt. Ein Fehler während der Extraktion kann unvollständige Dateien hinterlassen; prüfen Sie diese, bevor Sie die Extraktion in einem neuen Ausgabestammverzeichnis erneut versuchen. Zuvor fertiggestellte Ausgaben und die ursprünglichen ZIP-Dateien bleiben erhalten. Das Skript entfernt nur seine eigene private temporäre Kopie.
Lassen Sie bei einem Build-Job die nachgelagerte Verarbeitung stoppen, wenn das Skript einen Status ungleich null zurückgibt. Wählen Sie für eine manuelle erneute Ausführung ein neues Ausgabestammverzeichnis oder prüfen Sie zuerst die vorhandenen Dateien. Diese Entscheidung gehört in Ihren Workflow, nicht in eine unbeaufsichtigte Rückfrage zum Überschreiben.
