Текущая структура:

Cat and wolf
----- Volume 1.zip
----- Volume 2.zip
Cat and fox
---- Volume 01.rar
Rat and eagle
---- Rat and eagle 01.7x

Как видите, структура не соответствует простой схеме. Я хочу, чтобы это стало имя папки + номер . например.:

Cat and wolf 01.zip
Cat and wolf 02.zip
Cat and fox 01.rar
Rat and eagle 01.7z

Есть ли способ добиться такого результата?

0
izuna 4 Сен 2016 в 09:08

3 ответа

Лучший ответ

Это была довольно сложная проблема. Самым сложным аспектом было требование рекурсии. Но также, как вы отметили, шаблон именования непростой. Чтобы создать имя файла назначения, мы должны выделить конечный номер из основы имени исходного файла и объединить его с рекурсивно созданным префиксом имен каталогов.

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

function renameSubFilesToDest {

    local base;
    local dest;
    local sep;
    local prefix;

    local file;
    local fileBase;
    local -i num;
    local ext;
    local new;
    local -i rc;

    ## parse arguments
    if [[ $# -lt 1 ]]; then echo 'too few arguments.' >&2; return 1; fi;
    if [[ $# -gt 4 ]]; then echo 'too many arguments.' >&2; return 1; fi;
    base="$1"; ## unconditionally take base dir as 1st arg
    ## take destination dir as optional 2nd arg, default to base if not given
    if [[ $# -ge 2 ]]; then dest="$2"; else dest="$base"; fi;
    ## take name separator as optional 3rd arg, default to space if not given
    if [[ $# -ge 3 ]]; then sep="$3"; else sep=' '; fi;
    ## take new name prefix as optional 4th arg, default to empty string if not given
    if [[ $# -ge 4 ]]; then prefix="$4"; else prefix=''; fi;

    ## iterate over all objects in the base dir
    for file in "$base"/*; do
        fileBase="$(basename -- "$file";)";
        if [[ -d "$file" ]]; then
            ## recurse on dir, appending file basename and trailing sep to prefix
            renameSubFilesToDest "$file" "$dest" "$sep" "$prefix$fileBase$sep";
            rc=$?; if [[ $rc -ne 0 ]]; then return 1; fi;
        else
            ## don't process files at the first depth level
            if [[ -z "$prefix" ]]; then continue; fi;
            ## parse num and ext from file name using bash extended regular expressions
            if [[ ! "$fileBase" =~ ([1-9][0-9]*)\.([^.]+)$ ]]; then
                echo "warning: file \"$fileBase\" does not match expected pattern." >&2;
                continue;
            fi;
            num=${BASH_REMATCH[1]};
            ext=${BASH_REMATCH[2]};
            ## derive the final file name in dest
            new="$dest/$prefix$(printf %02d $num).$ext";
            printf '%s -> %s\n' "$file" "$new"; ## print status messages at run-time
            mv -i -- "$file" "$new";
            rc=$?; if [[ $rc -ne 0 ]]; then echo "error: mv [$rc]." >&2; return 1; fi;
        fi;
    done;

    ## for convenience, remove the dir if it is now empty
    find "$base" -maxdepth 0 -empty -delete;

    return 0;

} ## end renameSubFilesToDest()

Вот небольшая вспомогательная функция для предстоящей демонстрации, которая просто устанавливает структуру вашего входного файла в текущем каталоге:

function setupDemo {
    mkdir Cat\ and\ wolf Cat\ and\ fox Rat\ and\ eagle;
    touch Cat\ and\ wolf/Volume\ {1,2}.zip;
    touch Cat\ and\ fox/Volume\ 01.rar
    touch Rat\ and\ eagle/Rat\ and\ eagle\ 01.7z
} ## end setupDemo()

Вот демонстрация функции для структуры входного файла:

setupDemo; ## set up the file structure

find *; ## show it
## Cat and fox
## Cat and fox/Volume 01.rar
## Cat and wolf
## Cat and wolf/Volume 1.zip
## Cat and wolf/Volume 2.zip
## Rat and eagle
## Rat and eagle/Rat and eagle 01.7z

renameSubFilesToDest .; ## run the solution
## ./Cat and fox/Volume 01.rar -> ./Cat and fox 01.rar
## ./Cat and wolf/Volume 1.zip -> ./Cat and wolf 01.zip
## ./Cat and wolf/Volume 2.zip -> ./Cat and wolf 02.zip
## ./Rat and eagle/Rat and eagle 01.7z -> ./Rat and eagle 01.7z

find *; ## show the result
## Cat and fox 01.rar
## Cat and wolf 01.zip
## Cat and wolf 02.zip
## Rat and eagle 01.7z

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

mkdir -p level\ one/level\ two;
touch level\ one/level\ two/some\ file\ 7.txt;

find *;
## level one
## level one/level two
## level one/level two/some file 7.txt

renameSubFilesToDest . . _;
./level one/level two/some file 7.txt -> ./level one_level two_07.txt

find *;
## level one_level two_07.txt
0
bgoldst 4 Сен 2016 в 13:31

У всех ваших файлов есть такой шаблон: */LoremFolderName/Impsum dolore X.? в котором x - это число перед точкой [.] FileType и после пробела а также ? любой размер вашего файла;

По умолчанию для этого нет никакой программы, но если вы имеете в виду написание скрипта или программы, вы можете попробовать python в Linux, но в Windows я рекомендую простое программирование на C #;

Псевдокод:

    for each folder FLDR in /Path/To/RootFolder/
        {
            for each file in FLDR 
             {
               lastScpace=find last occurred space()
                  newName= copy file name from LastSpace to end
                   rename file name to (FLDR.Name+newName)
             }
        }
0
Vahid Chamarlou 4 Сен 2016 в 06:51

Вы можете сделать цикл по файлам и построить целевой файл.

find * -mindepth 1 | while read f; do
   target=$(echo "${f}" | sed 's#/# #g; s/Volume //')
   mv "$f" "${target}"
done

Первая часть команды sed, заменяющая косые черты, может быть заменена внутренней командой оболочки.

find * -mindepth 1 | while read f; do
   target=$(echo "${f//\// }" | sed 's/Volume //')
   mv "$f" "${target}"
done

Теперь вы хотите заменить число на число с дополнительным 0. Вы не объяснили требование, я предположил, что, когда у вас есть одна цифра перед последней точкой, мы должны добавить 0.

find * -mindepth 1 | while read f; do
   target=$(echo "${f//\// }" | 
      sed 's/Volume //; s/\([^0-9]\)\([0-9]\)\(\.[^.]*\)/\10\2\3/')
   mv "$f" "${target}"
done

Вы знаете, что таким образом можно потерять файлы?
Сравните x/Cat 2.zip, x/Cat 02.zip и x Cat/2.zip.
Другой вопрос, как найти уникальное имя файла, возможно, вы сначала попробуете что-то вроде

prospectTarget=${target}
counter=2
while [ -f "${target}" ]; do
   target=$(echo "${prospectTarget}_${counter}"
   ((counter++))
done
0
Walter A 4 Сен 2016 в 08:57