Как рекурсивно разархивировать архивы в каталоге и его подкаталогах из командной строки Unix?
Вопрос
А unzip
Команда не имеет возможности рекурсивно распаковывать архивы.
Если у меня есть следующая структура каталогов и архивов:
/Mother/Loving.zip /Scurvy/Sea Dogs.zip /Scurvy/Cures/Limes.zip
И я хочу разархивировать все архивы в каталоги с тем же именем, что и каждый архив:
/Mother/Loving/1.txt /Mother/Loving.zip /Scurvy/Sea Dogs/2.txt /Scurvy/Sea Dogs.zip /Scurvy/Cures/Limes/3.txt /Scurvy/Cures/Limes.zip
Какую команду или команды я бы отдал?
Важно, чтобы это не заглушало имена файлов, в которых есть пробелы.
Решение
Если вы хотите извлечь файлы в соответствующую папку, вы можете попробовать это
find . -name "*.zip" | while read filename; do unzip -o -d "`dirname "$filename"`" "$filename"; done;
Многопроцессорная версия для систем, способных обрабатывать большое количество операций ввода-вывода:
find . -name "*.zip" | xargs -P 5 -I fileName sh -c 'unzip -o -d "$(dirname "fileName")/$(basename -s .zip "fileName")" "fileName"'
Другие советы
Вот одно решение, которое включает в себя команду find и цикл while:
find . -name "*.zip" | while read filename; do unzip -o -d "`basename -s .zip "$filename"`" "$filename"; done;
Решение, которое правильно обрабатывает все имена файлов (включая символы новой строки) и извлекает их в каталог, который находится в том же месте, что и файл, только с удаленным расширением:
find . -name '*.zip' -exec sh -c 'unzip -o -d "${0%.*}" "$0"' '{}' ';'
Обратите внимание, что вы можете легко заставить его обрабатывать больше типов файлов (например, .jar
), добавив их с помощью -o
, например:
find . '(' -name '*.zip' -o -name '*.jar' ')' -exec ...
Вы можете использовать find вместе с флагом -exec в одной командной строке, чтобы выполнить задание.
find . -name "*.zip" -exec unzip {} \;
Что-то вроде Gunzip с флагом -r?....
Рекурсивно перемещайтесь по структуре каталогов.Если какое-либо из имен файлов, указанных в командной строке, является каталогом, gzip спустится в каталог и сожмет все найденные там файлы (или распакует их в случае Gunzip ).
Это работает отлично, как мы хотим:
Разархивируйте файлы:
find . -name "*.zip" | xargs -P 5 -I FILENAME sh -c 'unzip -o -d "$(dirname "FILENAME")" "FILENAME"'
Приведенная выше команда не создает дубликаты каталогов.
Удалите все zip-файлы:
find . -depth -name '*.zip' -exec rm {} \;
Если вы используете cygwin, синтаксис команды basename немного отличается.
find . -name "*.zip" | while read filename; do unzip -o -d "`basename "$filename" .zip`" "$filename"; done;
Я понимаю, что это очень старое решение, но оно было одним из первых результатов в Google, когда я искал решение чего-то подобного, поэтому я опубликую то, что я сделал здесь.Мой сценарий немного отличается, поскольку я просто хотел полностью взорвать банку вместе со всеми содержащимися в ней банками, поэтому я написал следующие функции bash:
function explode {
local target="$1"
echo "Exploding $target."
if [ -f "$target" ] ; then
explodeFile "$target"
elif [ -d "$target" ] ; then
while [ "$(find "$target" -type f -regextype posix-egrep -iregex ".*\.(zip|jar|ear|war|sar)")" != "" ] ; do
find "$target" -type f -regextype posix-egrep -iregex ".*\.(zip|jar|ear|war|sar)" -exec bash -c 'source "<file-where-this-function-is-stored>" ; explode "{}"' \;
done
else
echo "Could not find $target."
fi
}
function explodeFile {
local target="$1"
echo "Exploding file $target."
mv "$target" "$target.tmp"
unzip -q "$target.tmp" -d "$target"
rm "$target.tmp"
}
Обратите внимание <file-where-this-function-is-stored>
что необходимо, если вы храните это в файле, который не читается для неинтерактивной оболочки, как это случилось со мной.Если вы сохраняете функции в файле, загруженном в неинтерактивные оболочки (например, .bashrc
Я верю) можно бросить все source
заявление.Надеюсь, это кому-то поможет.
Маленькое предупреждение - explodeFile
также удаляет заархивированный файл, вы, конечно, можете изменить это, закомментировав последнюю строку.
Еще одним интересным решением будет:
DESTINY=[Give the output that you intend]
# Don't forget to change from .ZIP to .zip.
# In my case the files were in .ZIP.
# The echo were for debug purpose.
find . -name "*.ZIP" | while read filename; do
ADDRESS=$filename
#echo "Address: $ADDRESS"
BASENAME=`basename $filename .ZIP`
#echo "Basename: $BASENAME"
unzip -d "$DESTINY$BASENAME" "$ADDRESS";
done;