Jak „rozpakować” plik zip?

52

Rozpakowałem plik zip do niepustego folderu. Plik zip ma wiele plików i głęboką hierarchię, które połączyły się z istniejącym drzewem katalogu docelowego. Jak mogę usunąć pliki i katalogi, które zostały utworzone przez rozpakowanie bez niszczenia plików i katalogów, które już tam były? Oczywiście nadal mam plik zip, w którym się połączyłem, więc informacje tam są.

mafp
źródło
Umm, dziękuję za akceptację, ale to naprawdę był pomysł @ jjin. Nie wiedziałem o lqopcjach unzizp, po prostu dodałem kilka klasycznych sztuczek * nix wokół jego głównej odpowiedzi.
terdon
W porządku, tak naprawdę mnie to nie obchodzi. I tak dodałem własną wersję obsługi spacji.
jjlin
@terdon Tak ... Poparłem również odpowiedź jjlin, ale mogę zaakceptować tylko jedną odpowiedź.
mafp
Aby móc skorzystać z nich w przyszłości, zawsze wykonaj jedną z poniższych czynności, korzystając z nieznanego archiwum dowolnego formatu: 1) Wypakuj go do pustego katalogu lub 2) Najpierw wypisz go (rozpakuj -l) przed rozpakowaniem, aby zobaczyć, czy jest to tak nieprzyjemne. Archiwa utworzone bez katalogu najwyższego poziomu ze wszystkimi znajdującymi się w nim złymi formami. Kiedy skończysz ze smołą, tak naprawdę są one nazywane bombami smołowymi, więc myślę, że można to nazwać bombą zip.
Joe
@Joe Ma swoje zastosowania. Pakiety LaTeX mogą np. Mieć foo.tds.zipformę. Te zamki scalają się w drzewo TEXMF, co jest bardzo wygodne. Ale jeśli kiedykolwiek chcesz usunąć taki pakiet, masz do czynienia z problemem, który opisałem.
mafp

Odpowiedzi:

28

Odpowiedź jjlin jest właściwą drogą. Chcę tylko dodać kilka opcji dla katalogów:

  • Usuń wszystkie wyodrębnione pliki, bez katalogów :

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
  • Usuń tylko wyodrębnione pliki i puste katalogi

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

    Bez opcji rmdirusuwa tylko puste katalogi, pozostawia pliki i niepuste foldery w spokoju, dzięki czemu można je bezpiecznie uruchomić *.

  • Usuń wszystko , co zostało wyodrębnione, ale przed każdym usunięciem wyświetlaj monit o potwierdzenie:

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

    -iFlag spowoduje rmskłonić przed każdym usunięciem, można wybrać Tak lub Nie

  • Usuń wszystko , co wyodrębniono, w tym katalogi:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -rf "$n"; done
terdon
źródło
Usuwanie pustych katalogów można łatwo wykonać za pomocą find: find * -depth -type d -exec rmdir {} +i zignoruj ​​wszystkie Directory not emptywiadomości. Skrócenie tego może być legalne, find * -type d -deletegdy -deleteopcja się włącza, -depthale nie sprawdziłem, -deleteczy nie usunie niepustego katalogu.
Adrian Pronk
@AdrianPronk nie:find: cannot delete './foo': Directory not empty
terdon
28

Możesz użyć unzip -lqq <filename.zip>do wyświetlenia zawartości pliku zip; będzie to jednak zawierać dodatkowe informacje, które należy odfiltrować. Oto polecenie, które działa dla mnie:

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

Te awkekstrakty dowodzenia tylko nazwy plików i katalogów. Następnie wynik jest przekazywany xargsdo usunięcia wszystkiego. Sugeruję wykonanie suchego polecenia (tzn. Pominięcie xargs rm -rfczęści), aby upewnić się, że wyniki są poprawne.

Powyższe polecenie będzie miało problemy z obsługą ścieżek z białymi spacjami. Ta (bardziej skomplikowana) wersja powinna naprawić, że:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf
jjlin
źródło
Jest to już dość bliskie temu, co miałem na myśli, ale unzip -lqqwymienia także katalogi zawarte w zipie. Na razie zostawiłbym wszystkie katalogi same. Jak usunąć wszystkie puste katalogi z drzewa, może być kolejnym pytaniem.
mafp
@mafp To dobra uwaga na temat katalogów. Możesz dodać grep -v '/$'do potoku, aby pominąć usuwanie katalogów (wszystkie mają ukośnik końcowy, AFAICT).
jjlin
@terdon Właściwie myślę, że problem zaczyna się awkod, ponieważ wydrukowanie zaledwie 4 USD nie wydrukuje pełnej ścieżki.
jjlin
Nie sądzę, że powinieneś używać -ropcji rm: wydaje się, że prosi o kłopoty, szczególnie w połączeniu z tą -fopcją. -fW tym scenariuszu w ogóle nie skorzystałbym z tej opcji.
Adrian Pronk
1
@jjlin: grep -v '/$'pomija tylko wpisy katalogu w pliku ZIP. Nadal będą zawierać wpisy, które były zwykłymi plikami w pliku ZIP, ale były wcześniej istniejącymi katalogami w folderze docelowym. Z tego powodu rozsądnie byłoby pominąć-r
Adrian Pronk
11

Za pomocą przełącznika -Z1unzip wyświetli dokładnie jeden plik w wierszu (i nic więcej).

W ten sposób możesz użyć

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

aby usunąć wszystkie pliki wyodrębnione z pliku zip.

Komenda

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

usunie również katalogi, ale musisz być ostrożny. Jeśli katalogi już istniały przed rozpakowaniem pliku zip, wszystkie wcześniej istniejące pliki w tych katalogach również zostaną usunięte.


Jeśli i tak chcesz ponownie rozpakować plik zip, istnieje inne podejście, które gwarantuje poradzenie sobie z dziwnymi nazwami plików.

Najpierw rozpakuj plik zip, w którym pierwotnie miałeś go rozpakować:

unzip file.zip -d elsewhere

Teraz przejdź do katalogu, w którym przez pomyłkę rozpakowałeś pliki i wykonaj następujące polecenie:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f znajduje tylko pliki (bez katalogów).

  • %P\0jest ścieżką względną (bez elsewhere/), po której następuje znak null.

  • -0sprawia, że ​​xargs oddziela wiersze pustymi znakami. Jest to bardziej niezawodne, ponieważ - teoretycznie - nazwy plików mogą zawierać znaki nowego wiersza.


Aby poradzić sobie z pozostałymi katalogami, możesz wykonać polecenie:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d znajduje tylko katalogi.

  • -exec rmdir -p {} \;wykonuje się rmdir -p {}dla każdego znalezionego katalogu.

    {}to katalog, który został znaleziony, a -pprzełącznik powoduje, że rmdir usuwa również swoje puste katalogi nadrzędne.

  • 2> /dev/null pomija komunikaty o błędach, które pojawią się podczas próby usunięcia niepustych lub wcześniej usuniętych katalogów.


Powiązane strony podręcznika:

Dennis
źródło
+1 za zmuszanie mnie do przeczytania strony zipinfopodręcznika man.
terdon
Ojej, to trochę ułatwia. :)
jjlin
2

Oto jeszcze łatwiejsze i bezpieczniejsze (myślę) rozwiązanie

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

Co to robi: Polecenie unzip zapisane w cudzysłowie wyświetli listę zawartości oryginalnego pliku.

zip -m użyje następnie tej listy, aby dodać dodać, że każdy do getmeoutofhere.zip i usunąć go z oryginalnego katalogu (więc teoretycznie powinien to być indential do myoriginalfile.zip.

Minusem jest to, że unzip -lqq wygeneruje dodatkowy tekst, daty, godziny, rozmiar pliku itp. Spowoduje to, że zip -m wygeneruje komunikaty o błędach, ale nie powinno to mieć wpływu (chyba że masz mało prawdopodobny przypadek pliku z tym samym imię).

Pamiętaj, że nie spowoduje to usunięcia żadnych katalogów utworzonych podczas oryginalnego rozpakowywania.

David E.
źródło
Ciekawe podejście, zbadam dalej.
mafp
1

Jeśli pliki zostały wyodrębnione w taki sposób, że znacznik czasu modyfikacji w archiwum nie jest zachowany w wyodrębnionych kopiach (ale raczej wyodrębnione pliki mają swój zwykły czas modyfikacji), wówczas właściwym sposobem na zaatakowanie tego jest czas modyfikacji. Wszystkie wyodrębnione pliki mają nowszy znacznik czasu modyfikacji niż ostatnio zmodyfikowany istniejący plik w tym katalogu.

Oto prosta sytuacja.

Załóżmy, że żaden z istniejących plików w bieżącym katalogu nie został dotknięty przez co najmniej 24 godziny. Dlatego wszystko, co zostało zmodyfikowane w ciągu ostatnich 24 godzin, jest zbędne z pliku zip.

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

Znajduje to także niektóre katalogi, ale rmpozostawia je w spokoju. Można sobie z nimi poradzić za drugim razem:

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

Wszelkie ostatnio zmodyfikowane katalogi zostały zmodyfikowane przez zip. Jeśli rmdiruda się je usunąć, oznacza to, że są puste. Prawdopodobnie zostały przez nią utworzone puste katalogi, które zostały dotknięte przez zip: tzn. Pochodzą z archiwum. Nie możemy być w 100% pewni. Możliwe, że zadanie rozpakowania umieściło niektóre pliki w istniejącym katalogu, który był pusty.

Jeśli findziarnistość 24-godzinna nie jest wystarczająca do wykonania zadania, ponieważ pliki w drzewie zostały zbyt niedawno zmodyfikowane, to następnie rozważę coś prostego: załóżmy, że zadanie rozpakowania nie umieściło niczego w istniejących podkatalogach. Oznacza to, że wszystko, co zostało rozpakowane, jest albo plikiem na najwyższym poziomie, albo nowym podkatalogiem, którego wcześniej tam nie było, a zatem nie zawiera nic poza materiałem z zip. Następnie:

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

Teraz otwieramy filelistw edytorze tekstu i określamy pierwszy wpis na liście, który nie pochodzi z zip. Usuwamy ten wpis i wszystkie pozostałe po nim. Pozostały pliki i katalogi pochodzące z zip. Najpierw sprawdzamy wizualnie, czy występują takie problemy, jak spacje w nazwach i występowanie cudzysłowów, które należy usunąć. W razie potrzeby możemy dodać cytaty wokół wszystkiego: Następujące zakłada, że ​​używasz Vima:

:%s/.*/"&"/

Następnie połącz to wszystko w wielką linię:

:%j

Teraz wstaw rm -rfprzed nim:

Irm - rf<ESC>

Uruchom linię pod kursorem jako polecenie powłoki:

!!sh<Enter>

Zdecydowanie nie zautomatyzowałbym kroków tego zadania ze względu na ryzyko usunięcia plików, które już tam były, lub zepsucia z powodu problemów z nazwami plików.

Jeśli zamierzasz wybrać oczywistą drogę uzyskania listy ścieżek w pliku zip, a następnie przechwyć ją do pliku, przejrzyj ją bardzo uważnie i przekształć w usunięcie po przeprowadzeniu niezbędnej edycji.

Kaz
źródło