Эквивалент tar "--strip-components = 1" в unzip?

22457
frigg

У меня есть сценарий, который извлекает tar.gz-файл в указанную подпапку mysubfolder :

mkdir mysubfolder; tar --extract --file=sourcefile.tar.gz --strip-components=1 --directory=mysubfolder; 

Есть ли эквивалентный способ сделать это с zip-файлом?

41
Просто используйте bsdtar drizzt 8 лет назад 2

2 ответа на вопрос

22
MestreLion

Как сказал Матиас, unzipтакой опции нет, но однострочный скрипт bash может сделать эту работу.

Проблема в том, что наилучший подход зависит от формата вашего архива. Решение, которое предполагает один каталог верхнего уровня, с треском провалится, если контент находится непосредственно в корне архива (подумайте о /a/foo /b/foo /fooхаосе разборки /aи /b).

И тот же сбой происходит с tar --strip-component. Не существует единого решения для всех.

Итак, для удаления корневого каталога, предполагая, что есть один (и только один):

unzip -d "$dest" "$zip" && f=("$dest"/*) && mv "$dest"/*/* "$dest" && rmdir "$" 

Просто убедитесь, что файлы / каталоги второго уровня не имеют одинакового имени родителя верхнего уровня (например, /foo/foo). Но /foo/bar/fooи /foo/bar/barвсе в порядке. Если это так, или вы просто хотите быть в безопасности, вы можете использовать временный каталог для извлечения:

temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" && mv "$temp"/*/* "$dest" && rmdir "$temp"/* "$temp" 

Если вы используете Bash, вы можете проверить, является ли верхний уровень одним каталогом или нет:

f=("$temp"/*); (( ${#f[@]} == 1 )) && [[ -d "$" ]] && echo "Single dir!" 

Говоря о Bash, вы должны dotglobвключить скрытые файлы, и вы можете обернуть все в одну удобную функцию:

unzip-strip() ( local zip=$1 local dest=$ local temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" && shopt -s dotglob && local f=("$temp"/*) && if (( ${#f[@]} == 1 )) && [[ -d "$" ]] ; then mv "$temp"/*/* "$dest" else mv "$temp"/* "$dest" fi && rmdir "$temp"/* "$temp" ) 

Теперь вставьте это в свой ~/.bashrcили, ~/.profileи вам больше никогда не придется беспокоиться об этом. Просто используйте как:

unzip-strip sourcefile.zip mysubfolder 

(обратите внимание, он будет создан автоматически mysubfolderдля вас, если он не существует)

Это не распакует в существующую структуру каталогов, как я надеялся (я пытался использовать. Вместо mysubfolder). В итоге я просто распаковал (unzip zip-with-top-dir.zip) и затем скопировал (cp -rv extract-top-zip-dir / *.). catgofire 7 лет назад 0
2
Mathias Bynens

Я не смог найти такой вариант на страницах справочникаunzip, поэтому боюсь, что это невозможно. :(

Однако (в зависимости от ситуации) вы можете обойти это. Например, если вы уверены, что единственный каталог верхнего уровня в zip-файле имеет имя, foo-за которым следует номер версии, вы можете сделать что-то вроде этого:

cd /tmp unzip /path/to/file.zip cd foo-* cp -r . /path/to/destination/folder 
Хороший подход, но немного неполный: у вас все равно будет foo * dir с полным извлеченным контентом. MestreLion 11 лет назад 0
Да, я специально не добавил `rm -rf foo- *`, поскольку это потенциально опасно. Что, если там уже была папка с именем `foo-bar`? Обратите внимание, что извлечение выполняется в папке `/ tmp`, которая время от времени очищается автоматически. Mathias Bynens 11 лет назад 0
Вот почему я связал операции с помощью `&&`: данный шаг происходит только в том случае, если предыдущий шаг был успешным, поэтому последний (`rm`) выполняется только в том случае, если * все * шаги выполнены без ошибок. MestreLion 11 лет назад 0
Вот почему никогда не следует использовать `/ tmp / some-hardcoded-folder-name` в качестве временной папки, а вместо этого следует использовать` mktemp`: это гарантирует, что такой папки не будет. Проверьте мой ответ ниже. MestreLion 11 лет назад 1
ТИЛ про `МКТЕМП`. Спасибо! Mathias Bynens 11 лет назад 0

Похожие вопросы