
По сути, у меня есть набор каталогов, которые постоянно заполняются .rar, и мне нужно иметь возможность извлекать их на месте с автоматическим удалением оставшихся .(rar|rXX) файлов. Как мне это сделать?
Примечание: я не могу удалить их все после завершения, их придется удалить, поскольку скрипт завершает один набор rar.
Пример структуры каталога:
/
/folder1/
/file1.rar
/file1.r00
/file1.r01
/folder2/
/sub.folder1/
/file2.part001.rar (contains a directory "file2")
/file2.part002.rar
/file2.part003.rar
/sub.folder2/
/file3.rar
/file3.r00
/file3.r01
Ожидаемый результат:
/
/folder1/
/file1.ext
/folder2/
/sub.folder1/
/file2/
/file2.ext
/sub.folder2/
/file3.ext
решение1
Вариант для Bash:
#!/bin/bash
if [ ! -d "${1}" ]; then
printf '%s: error: mandatory argument is not a valid directory.' "${0##*/}" 1>&2
exit 1
fi
shopt -s extglob
for i in "${1}"/**/*.rar; do
cd -- "${i%/*}"
file=${i##*/}
unrar e -- "${file}" && rm -- @(${file}|${file%.rar}.r[[:digit:]][[:digit:]])
cd - >/dev/null
done
Архивы RAR также могут быть «законно» названы eg myarchive.part001.rar
и т. д., и это должно быть обработано отдельно каким-то образом, если такие типы архивов существуют. Прагматичный способ решения этой проблемы — заменить
unrar e -- "${file}" && rm -- @(${file}|${file%.rar}.r[[:digit:]][[:digit:]])
с
unrar e -o- -- "${file}"
if [ $? -eq 0 -o $? -eq 10 ]; then
rm -- @(${file}|${file%.rar}.r[[:digit:]][[:digit:]])
fi
Однако следует помнить, что при этом будут удалены файлы RAR, даже если выдается код ошибки 10 («Нет файлов для извлечения»), поэтому этот метод обычно не рекомендуется использовать в неизмененном виде.
В общем, может быть лучше просто хранить материал в архивной форме и вместо этого использовать декомпрессию на лету или что-то подобное, когда вы хотите его использовать. Могут быть и другие преимущества сохранения архивного формата.
решение2
Я пишу это с потолка, так что это непроверенный код, но он должен направить вас на верный путь. Он в основном проходит по дереву, на которое вы его укажете, в поисках файлов .rar. Найдя один, он распакует его на месте и удалит исходный архив, если unrar не вернет ненулевое значение. При попадании на папку функция просто вызовет саму себя, что сделает ее рекурсивной.
#!/bin/bash
[[ ! -d "$1" ]] && echo "Please point me at a directory!" && exit 1
function recursively_extract_all_rars_in() {
local x=`pwd` f
cd "$1"
for f in (*); do
[[ -d "$f" ]] && recursively_extract_all_rars_in "$f"
[[ -f "$f" ]] && [[ "$f" =~ "*.rar" ]] && unrar e "$f" && rm ${f%%.*}.r??
done
cd "$x"
}
recursively_extract_all_rars_in "$1"
Имя функции совершенно произвольно. Мне нравится, чтобы они читались как настоящий английский при вызове с их аргументами. [[ -d /path ]]
возвращает true, если путь существует и является каталогом. -f
выполняет соответствующие действия для файлов. [[ "string" =~ "pattern"]]
является башизмом, который позволяет сопоставлять шаблоны в строках. Он работает в основном так же, как шаблоны glob.
Строка local x=pwd f
может показаться непонятной, но она просто определяет две локальные переменные: одну с именем x, для хранения pwd
, и одну с именем f, неинициализированную (она инициализируется в цикле for ниже, я просто объявляю ее здесь, чтобы она была локальной).
Сохранение pwd
и возврат к нему, если ваша функция его использует, cd
— это Хорошая Вещь (tm).
Обратите внимание, что использование вывода ls
программного кода, как правило, является плохим приемом, и вам следует избегать его как вредителя, отдавая предпочтение find
.Если какое-либо из имен ваших файлов содержит пробел, использование ls в вашем скрипте приведет к серьезным ошибкам.Вы были предупреждены.
ЗШ
Я не уверен, что вы сможете сделать то же самое в Bash, но в ZSH я бы поместил следующее куда-нибудь.zshrc
function recursive_unrar() {
for f in **/*.rar; do
local cwd=`pwd` fbn=${f##*/}
cd "${f%/*}"
unrar e "$fbn"
rm "${fbn%%.*}.r{01..99} $fbn"
cd "$cwd"
done
}
А затем просто вызовите его из соответствующей папки.