Wie kann ich eine Zip-Datei entpacken?

52

Ich habe eine Zip-Datei in einen nicht leeren Ordner extrahiert. Die ZIP-Datei enthält viele Dateien und eine tiefe Hierarchie, die mit dem vorhandenen Baum des Zielverzeichnisses zusammengeführt wird. Wie kann ich die Dateien und Verzeichnisse entfernen, die durch Entpacken erstellt wurden, ohne die bereits vorhandenen Dateien und Verzeichnisse zu zerstören? Natürlich habe ich immer noch die Zip-Datei, in der ich zusammengeführt habe, also sind die Informationen dort.

mafp
quelle
Umm danke fürs akzeptieren, aber es war wirklich @jjins Idee. Ich kannte die lqOptionen für nicht unzizp, ich habe nur einige klassische * nix-Tricks um seine Hauptantwort hinzugefügt.
Terdon
Das ist okay, es interessiert mich nicht so sehr. Ich habe trotzdem meine eigene Version von Whitespace-Handling hinzugefügt.
JJLIN
@terdon Ja ... Ich habe auch die Antwort von jjlin positiv bewertet, aber ich kann nur eine Antwort akzeptieren.
Mafp
Führen Sie zum späteren Nachschlagen immer einen der folgenden Schritte mit einem unbekannten Archiv eines beliebigen Formats aus: 1) Extrahieren Sie es in ein leeres Verzeichnis oder 2) Listen Sie es zuerst auf (unzip -l), bevor Sie es extrahieren, damit Sie sehen können, ob es so böse ist. Archive, die ohne ein Verzeichnis der obersten Ebene erstellt wurden, sind in einer schlechten Form. Wenn man mit Teer fertig ist, nennt man sie eigentlich Teerbomben, also könnte man das eine Zip-Bombe nennen.
Joe
@ Joe Es hat seinen Nutzen. LaTeX-Pakete können beispielsweise in einer foo.tds.zipForm vorliegen. Diese Reißverschlüsse verschmelzen zu einem TEXMF-Baum, was sehr praktisch ist. Wenn Sie jedoch jemals ein solches Paket entfernen möchten, stehen Sie vor dem von mir beschriebenen Problem.
Mafp

Antworten:

28

jjlins antwort ist der richtige weg. Ich möchte nur ein paar Auswahlmöglichkeiten für Verzeichnisse hinzufügen:

  • Löschen Sie alle extrahierten Dateien, keine Verzeichnisse :

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
  • Löschen Sie nur extrahierte Dateien und leere Verzeichnisse

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done; rmdir *

    Wenn keine Optionen rmdirverfügbar sind, werden nur leere Verzeichnisse gelöscht. Dateien und nicht leere Ordner bleiben in Ruhe, sodass Sie sie sicher ausführen können *.

  • Löschen Sie alles , was extrahiert wurde, aber fordern Sie vor jedem Löschen eine Bestätigung an:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -ri "$n"; done; rmdir *

    Das -iFlag rmfordert vor jedem Entfernen dazu auf, Ja oder Nein zu wählen.

  • Löschen Sie alles , was extrahiert wurde, einschließlich der Verzeichnisse:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -rf "$n"; done
terdon
quelle
Das Löschen leerer Verzeichnisse erfolgt ganz einfach mit find: find * -depth -type d -exec rmdir {} +und ignoriert alle Directory not emptyNachrichten. Es könnte legal sein, dies zu verkürzen, find * -type d -deletewenn die -deleteOption aktiviert wird, -depthaber ich habe nicht überprüft, dass -deleteein nicht leeres Verzeichnis nicht gelöscht wird.
Adrian Pronk
@ AdrianPronk tut es nicht:find: cannot delete './foo': Directory not empty
Terdon
28

Mit können Sie unzip -lqq <filename.zip>den Inhalt der ZIP-Datei auflisten. Dies wird jedoch einige irrelevante Informationen enthalten, die herausgefiltert werden müssen. Hier ist ein Befehl, der für mich funktioniert:

unzip -lqq file.zip | awk '{print $4;}' | xargs rm -rf

Der awkBefehl extrahiert nur die Namen der Dateien und Verzeichnisse. Dann wird das Ergebnis an übergeben xargs, um alles zu löschen. Ich empfehle, zuerst einen Probelauf des Befehls durchzuführen (dh den xargs rm -rfTeil wegzulassen ), um sicherzustellen, dass die Ergebnisse korrekt sind.

Der obige Befehl hat Probleme mit Pfaden mit Leerzeichen. Diese (kompliziertere) Version sollte Folgendes beheben:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf
jjlin
quelle
Dies entspricht schon ziemlich genau dem, was ich mir vorgestellt habe, unzip -lqqlistet aber auch die im zip enthaltenen Verzeichnisse auf. Vorerst würde ich alle Verzeichnisse in Ruhe lassen. Das Löschen aller leeren Verzeichnisse in einem Baum kann eine weitere Frage sein.
Mafp
@mafp Das ist ein guter Punkt bei den Verzeichnissen. Sie können grep -v '/$'in die Pipeline hinzufügen , um das Löschen der Verzeichnisse (die alle einen abschließenden Schrägstrich (AFAICT) haben) zu überspringen.
JJLIN
@terdon Eigentlich glaube ich, dass das Problem am beginnt awk, da beim Drucken von nur 4 Euro nicht der vollständige Pfad gedruckt wird.
JJLIN
Ich denke nicht, dass Sie die -rOption von rm verwenden sollten: das scheint Probleme zu bereiten, besonders wenn es mit der -fOption kombiniert wird . -fIn diesem Szenario würde ich die Option überhaupt nicht verwenden .
Adrian Pronk
1
@jjlin: Lässt grep -v '/$'nur Verzeichniseinträge in der ZIP-Datei aus. Sie enthalten weiterhin Einträge, die reine Dateien in der ZIP-Datei waren, jedoch bereits vorhandene Verzeichnisse im Zielordner. Aus diesem Grund wäre es klug, wegzulassen-r
Adrian Pronk
11

Mit dem Schalter -Z1wird beim Entpacken genau eine Datei pro Zeile aufgelistet (und sonst nichts).

Auf diese Weise können Sie verwenden

unzip -Z1 | xargs -I {} rm '{}'

um alle aus der zip-Datei extrahierten Dateien zu löschen.

Der Befehl

unzip -Z1 | xargs -I {} rm -rf '{}'

löscht auch Verzeichnisse, aber Sie müssen vorsichtig sein. Wenn die Verzeichnisse vor dem Extrahieren der ZIP-Datei bereits vorhanden waren, werden auch alle in diesen Verzeichnissen vorhandenen Dateien gelöscht.


Wenn Sie die ZIP-Datei trotzdem erneut extrahieren, gibt es einen anderen Ansatz, der mit seltsamen Dateinamen umgeht.

Extrahieren Sie zuerst die Zip-Datei, in die Sie sie ursprünglich extrahieren wollten:

unzip file.zip -d elsewhere

Wechseln Sie nun in das Verzeichnis, in das Sie versehentlich die Dateien extrahiert haben, und führen Sie den folgenden Befehl aus:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f findet nur Dateien (keine Verzeichnisse).

  • %P\0ist der relative Pfad (ohne elsewhere/), gefolgt von einem Nullzeichen.

  • -0Legt fest, dass XARGs Zeilen durch Nullzeichen trennen. Dies ist zuverlässiger, da Dateinamen theoretisch Zeilenumbrüche enthalten können.


Um mit verbliebenen Verzeichnissen umzugehen, können Sie den folgenden Befehl ausführen:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d findet nur Verzeichnisse.

  • -exec rmdir -p {} \;wird rmdir -p {}für jedes gefundene Verzeichnis ausgeführt.

    {}ist das gefundene Verzeichnis, und der -pSchalter veranlasst rmdir, auch die leeren übergeordneten Verzeichnisse zu entfernen.

  • 2> /dev/null Unterdrückt die Fehlermeldungen, die beim Versuch auftreten, nicht leere oder zuvor gelöschte Verzeichnisse zu löschen.


Verwandte Manpages:

Dennis
quelle
+1 dafür, dass ich die zipinfoManpage von gelesen habe .
Terdon
Nun, meine Güte, das macht es ein wenig einfacher. :)
jjlin
2

Hier ist eine noch einfachere und sicherere (glaube ich) Lösung

zip -m getmeoutofhere.zip `unzip -lqq myoriginalzipfile.zip`
rm getmeoutofhere.zip

Vorgehensweise: Mit dem Befehl "Entpacken in Anführungszeichen" wird eine Liste der in Ihrer Originaldatei enthaltenen Elemente erstellt.

zip -m verwendet diese Liste dann, um add that each zu getmeoutofhere.zip hinzuzufügen und aus dem Originalverzeichnis zu entfernen (theoretisch sollte es also indentiell zu myoriginalfile.zip sein).

Der Nachteil ist, dass unzip -lqq zusätzlichen Text, Datumsangaben, Uhrzeiten, Dateigröße usw. erzeugt. Dies führt dazu, dass zip -m Fehlermeldungen ausgibt, die sich jedoch nicht auswirken sollten (es sei denn, Sie haben den unwahrscheinlichen Fall einer Datei mit derselben Name).

Beachten Sie, dass dadurch keine Verzeichnisse entfernt werden, die beim ursprünglichen Entpacken erstellt wurden.

David E.
quelle
Interessanter Ansatz, wird weiter erforschen.
Mafp
1

Wenn Sie die Dateien so extrahiert haben, dass der Änderungszeitstempel im Archiv nicht in den extrahierten Kopien erhalten bleibt (die extrahierten Dateien haben jedoch die übliche Änderungszeit), können Sie dies über die Änderungszeit angreifen. Alle extrahierten Dateien haben einen neueren Änderungszeitstempel als die zuletzt geänderte vorhandene Datei in diesem Verzeichnis.

Hier ist eine einfache Situation.

Angenommen, keine der im aktuellen Verzeichnis vorhandenen Dateien wurde mindestens 24 Stunden lang berührt. Alles, was in den letzten 24 Stunden geändert wurde, ist daher Müll aus der Zip-Datei.

$ find . -mtime -1 -print0 | xargs -0 rm

Dadurch werden auch einige Verzeichnisse gefunden, die jedoch rmin Ruhe gelassen werden. Sie können in einem zweiten Durchgang behandelt werden:

$ find . -mtime 1 -type d -print 0 | xargs -0 rmdir

Alle Verzeichnisse, die kürzlich geändert wurden, wurden vom zip geändert. Wenn sie rmdirerfolgreich entfernt wurden, sind sie leer. Leere Verzeichnisse, die von zip berührt wurden, wurden wahrscheinlich von zip erstellt: dh kamen aus dem Archiv. Wir können nicht 100% sicher sein. Es ist möglich, dass beim Entpacken einige Dateien in ein vorhandenes Verzeichnis verschoben wurden, das leer war.

Wenn finddie 24-Stunden-Granularität für den Job nicht ausreicht, weil die Dateien im Baum zu spät geändert wurden, würde ich als Nächstes etwas Einfaches betrachten: Angenommen, der Entpack-Job hat nichts in vorhandene Unterverzeichnisse verschoben. Das heißt, alles, was entpackt wurde, ist entweder eine Datei auf der obersten Ebene oder ein neues Unterverzeichnis, das vorher nicht vorhanden war und daher nichts als Material aus dem Zip enthält. Dann:

# list directory in descending order of modification time
$ ls -1t > filelist  # descending order of modification time

Nun öffnen wir uns filelistin einem Texteditor und ermitteln den ersten Eintrag in der Liste, der nicht aus der zip stammt. Wir löschen diesen Eintrag und alles andere danach. Was bleibt, sind die Dateien und Verzeichnisse, die aus dem Zip stammen. Zunächst untersuchen wir visuell, ob Leerzeichen in den Namen oder Anführungszeichen vorhanden sind, die maskiert werden müssen. Bei Bedarf können wir dann alles in Anführungszeichen setzen: Im Folgenden wird davon ausgegangen, dass Sie Vim verwenden:

:%s/.*/"&"/

Dann verbinde alles zu einer großen Linie:

:%j

Fügen Sie nun rm -rfdavor ein:

Irm - rf<ESC>

Führen Sie die Zeile unter dem Cursor als Shell-Befehl aus:

!!sh<Enter>

Auf jeden Fall würde ich die Schritte dieser Aufgabe nicht automatisieren, da die Gefahr besteht, dass bereits vorhandene Dateien gelöscht oder aufgrund von Dateinamenproblemen Fehler gemacht werden.

Wenn Sie den naheliegenden Weg gehen, eine Liste der Pfade in der ZIP-Datei zu erhalten, erfassen Sie diese in einer Datei, überprüfen Sie sie sorgfältig und wandeln Sie sie nach der erforderlichen Bearbeitung in eine Löschung um.

Kaz
quelle