Как «извлечь» файл zip?

52

Я извлек zip-файл в непустую папку. Zip-файл содержит множество файлов и глубокую иерархию, которые объединены с существующим деревом целевого каталога. Как я могу удалить файлы и каталоги, которые были созданы, разархивировав файлы, не разрушая уже существующие файлы и каталоги? Конечно, у меня все еще есть zip-файл, который я слил, поэтому информация там.

mafp
источник
Хм, спасибо за согласие, но это была идея @ jjin. Я не знал lqвариантов unzizp, я просто добавил несколько классических * nix трюков вокруг его основного ответа.
Тердон
Ничего страшного, мне все равно. В любом случае я добавил свою собственную версию обработки пробелов.
Jjlin
@terdon Да ... Я тоже проголосовал за ответ jjlin, но могу принять только один ответ.
Mafp
Для дальнейшего использования всегда выполняйте одно из следующих действий с незнакомым архивом любого формата: 1) Извлеките его в пустой каталог или 2) Сначала перечислите его (unzip -l), прежде чем извлекать, чтобы вы могли увидеть, насколько это противно. Архивы, сделанные без каталога верхнего уровня со всем, что находится под этим, являются плохой формой. Когда это делается с помощью tar, их на самом деле называют смоляными бомбами, так что я думаю, это можно назвать бомбой на молнии.
Джо
@Joe У этого есть его использование. Например, пакеты LaTeX могут быть в foo.tds.zipформе. Эти молнии сливаются в дерево TEXMF, что очень удобно. Но если вы когда-нибудь захотите удалить такой пакет, вы столкнетесь с проблемой, которую я описал.
13

Ответы:

28

Ответ JJLIN - путь. Я просто хочу добавить несколько вариантов для каталогов:

  • Удалить все извлеченные файлы, без каталогов :

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
    
  • Удалить только извлеченные файлы и пустые каталоги

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

    Без параметров rmdirудаляет только пустые каталоги, он оставляет файлы и непустые папки в одиночестве, чтобы вы могли безопасно запускать его *.

  • Удалите все извлеченное, но запрашивайте подтверждение перед каждым удалением:

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

    -iФлаг будет вызывать rmк строке перед каждым удалением, вы можете выбрать Да или Нет

  • Удалить все извлеченные, включая каталоги:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -rf "$n"; done
    
terdon
источник
Удаление пустых каталогов легко сделать с помощью find: find * -depth -type d -exec rmdir {} +и игнорировать все Directory not emptyсообщения. Это может быть законно, чтобы сократить это до find * -type d -deleteвключения -deleteопции, -depthно я не проверил, что -deleteне удалит непустой каталог.
Адриан Пронк
@AdrianPronk это не так:find: cannot delete './foo': Directory not empty
Terdon
28

Вы можете использовать unzip -lqq <filename.zip>для просмотра содержимого zip-файла; это будет включать некоторую постороннюю информацию, которую вам нужно будет отфильтровать. Вот команда, которая работает для меня:

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

Команда awkизвлекает только имена файлов и каталогов. Затем результат передается, xargsчтобы удалить все. Я предлагаю сначала выполнить пробный прогон команды (то есть, пропустив xargs rm -rfчасть), чтобы убедиться, что результаты верны.

Приведенная выше команда будет иметь проблемы с путями, которые имеют пробелы. Эта (более сложная) версия должна исправить это:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf
jjlin
источник
Это уже довольно близко к тому, что я имел в виду, но unzip -lqqперечисляет также каталоги, содержащиеся в zip. Сейчас я бы оставил все каталоги в покое. Как удалить все пустые каталоги в дереве, может быть последующим вопросом.
Mafp
@mafp Это хороший момент о каталогах. Вы можете добавить grep -v '/$'в конвейер, чтобы пропустить удаление каталогов (у которых есть косая черта, AFAICT).
Jjlin
@terdon На самом деле я думаю, что проблема начинается с awk, так как печать всего за 4 доллара не напечатает полный путь.
Jjlin
Я не думаю, что вам следует использовать -rопцию rm: кажется, что возникают проблемы, особенно в сочетании с -fопцией. Я бы не стал использовать эту -fопцию вообще в этом сценарии.
Адриан Пронк
1
@jjlin: grep -v '/$'пропустит только записи каталога в ZIP-файле. Они по-прежнему будут содержать записи, которые были простыми файлами в ZIP-файле, но были уже существующими каталогами в целевой папке. По этой причине было бы разумно опустить это-r
Адриан Пронк
11

С переключателем -Z1unzip будет перечислять ровно один файл в строке (и ничего больше).

Таким образом, вы можете использовать

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

удалить все файлы, извлеченные из ZIP-файла.

Команда

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

также удалит каталоги, но вы должны быть осторожны. Если каталоги уже существовали до распаковки zip-файла, все существующие в этих каталогах файлы также будут удалены.


Если вы все равно собираетесь повторно извлечь zip-файл, есть другой подход, который гарантированно справится со странными именами файлов.

Сначала распакуйте zip-файл, где вы изначально хотели его распаковать:

unzip file.zip -d elsewhere

Теперь перейдите в каталог, в который вы по ошибке извлекли файлы, и выполните следующую команду:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f только находит файлы (без каталогов).

  • %P\0это относительный путь (без elsewhere/), за которым следует нулевой символ.

  • -0делает XARGS отдельными строками с нулевыми символами. Это более надежно, поскольку в теории имена файлов могут содержать символы новой строки.


Для работы с оставшимися каталогами вы можете выполнить команду:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d только находит каталоги.

  • -exec rmdir -p {} \;выполняется rmdir -p {}для каждого каталога, который был найден.

    {}это каталог, который был найден, и -pпереключатель заставляет rmdir также удалить свои пустые родительские каталоги.

  • 2> /dev/null подавляет сообщения об ошибках, которые возникают при попытке удаления непустых или ранее удаленных каталогов.


Связанные справочные страницы:

Деннис
источник
+1 за то, что заставил меня прочитать zipinfoстраницу справочника.
Тердон
Ну и дела, это делает это немного легче. :)
jjlin
2

Вот еще более простое и безопасное (я думаю) решение

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

Что это делает: команда unzip с кавычками выдаст список того, что было в вашем исходном файле.

Затем zip -m будет использовать этот список, чтобы добавить add для каждого в getmeoutofhere.zip и удалить его из исходного каталога (поэтому теоретически он должен быть независим от myoriginalfile.zip.

Недостатком является то, что unzip -lqq создаст дополнительный текст, даты, время, размер файла и т. Д. Это приведет к тому, что zip -m выдаст сообщения об ошибках, но это не должно иметь никакого влияния (если только у вас нет маловероятного случая файла с таким же имя).

Обратите внимание, что это не приведет к удалению любых каталогов, которые были созданы во время первоначального распаковки.

Дэвид Э.
источник
Интересный подход, рассмотрим дальше.
Mafp
1

Если вы извлекли файлы таким образом, что метка времени изменения в архиве не сохраняется в извлеченных копиях (а извлеченные файлы имеют свое обычное время модификации), то правильный способ атаковать это - через время модификации. Все извлеченные файлы имеют более новую метку времени изменения, чем последний измененный существующий файл в этом каталоге.

Вот простая ситуация.

Предположим, что ни один из существующих файлов в текущем каталоге не был затронут в течение как минимум 24 часов. Поэтому все, что было изменено за последние 24 часа, является ненужным из zipfile.

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

Это также найдет некоторые каталоги, но rmоставит их в покое. С ними можно разобраться во втором проходе:

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

Любые каталоги, которые были недавно изменены, были изменены почтовым индексом. Если rmdirуспешно удаляет их, это означает, что они пусты. Пустые каталоги, которые были затронуты zip, вероятно, были созданы им: т.е. пришли из архива. Мы не можем быть на 100% уверены. Возможно, что задание распаковки поместило некоторые файлы в существующий каталог, который был пустым.

Если find24-часовая детализация недостаточно хороша для задания, поскольку файлы в дереве были изменены слишком недавно, то я бы затем подумал о чем-то простом: предположим, что задание распаковки ничего не помещало в существующие подкаталоги. То есть, все, что было распаковано, - это либо файл на верхнем уровне, либо новый подкаталог, которого раньше не было, который поэтому содержит только материал из zip-архива. Затем:

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

Теперь мы открываем filelistв текстовом редакторе и определяем первую запись в списке, которая не пришла из zip. Мы удаляем эту запись и все остальное после нее. То, что осталось, это файлы и каталоги, которые пришли из zip. Сначала мы визуально проверяем наличие проблем, таких как пробелы в именах, и вхождение кавычек, которые необходимо экранировать. Затем мы можем добавить кавычки вокруг всего, если необходимо: следующее предполагает, что вы используете Vim:

:%s/.*/"&"/

Затем объедините все это в большую строку:

:%j

Теперь вставьте rm -rfперед ним:

Irm - rf<ESC>

Запустите строку под курсором как команду оболочки:

!!sh<Enter>

Определенно, я бы не стал автоматизировать шаги этой задачи из-за риска стирания файлов, которые уже были там, или из-за проблем с именами файлов.

Если вы собираетесь пойти очевидным путем получения списка путей в zip-архиве, запишите его в файл, внимательно его просмотрите и преобразуйте в удаление после выполнения любого необходимого редактирования.

Kaz
источник