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

49

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

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

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

Фригг
источник
2
Просто используйте bsdtar
drizzt

Ответы:

26

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

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

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

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

unzip -d "$dest" "$zip" && f=("$dest"/*) && mv "$dest"/*/* "$dest" && rmdir "${f[@]}"

Просто убедитесь, что файлы / каталоги второго уровня не имеют одинакового имени родителя верхнего уровня (например, /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 "${f[0]}" ]] && echo "Single dir!"

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

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

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

unzip-strip sourcefile.zip mysubfolder

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

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

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

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

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

Вы можете использовать -jдля мусорных путей (не создавать каталоги). Это рекомендуется только для довольно распространенных одноуровневых архивов. Архивы с многоуровневой структурой каталогов будут сглажены - это может даже привести к конфликтам имен для извлекаемых файлов.

Со страницы руководства по распаковке:

   -j     junk  paths.   The  archive's directory structure is not recreated; all files are deposited in the
          extraction directory (by default, the current one).
Педро Родригес
источник