Автоматическое копирование папок до достижения определенного предела

Привет.

1 - Допустим, у меня есть около 500 папок переменного размера общим размером 100 ГБ.

2 - Я хочу автоматически распределять эти папки по другим папкам, пока не будет достигнут размер в 700 МБ с наилучшей оптимизацией места.

Пример: В папке "CD--01" я хочу иметь максимально возможное количество папок, не превышая ограничение в 700 МБ, и так далее в "CD--02", "CD--03"...

Есть ли инструмент, который позволяет мне делать это «на лету», или мне придется самому писать код?

Спасибо


person Joao Heleno    schedule 27.12.2008    source источник
comment
Сделать это оптимально — задача о рюкзаке. Не может быть решена для любого нетривиального набора данных за разумное время. Неоптимально жизнеспособно.   -  person Sparr    schedule 28.12.2008
comment
нет, файлы не имеют значения. это имеет большое значение.   -  person msb    schedule 24.02.2017


Ответы (5)


Это очень наивное и плохо закодированное решение, но оно работает. Мой bash-fu не силен, но сценарий оболочки кажется лучшим способом решить эту проблему.

#!/bin/bash
dirnum=1
for i in *
    do
    if [ `du -b -s "$i" | cut -f 1` -gt 700000000 ]
        then
        echo "$i is too big for a single folder, skipping"
        continue
    fi
    if [ ! -d "CD_$dirnum" ]
        then
        echo "creating directory CD_$dirnum"
        mkdir "CD_$dirnum"
    fi
    echo "moving $i to CD_$dirnum"
    mv "$i" "CD_$dirnum"
    if [ `du -b -s "CD_$dirnum" | cut -f 1` -gt 700000000 ]
        then
        echo "CD_$dirnum is too big now"
        mv "CD_$dirnum/$i" .
        let "dirnum += 1"
        if [ ! -d "CD_$dirnum" ]
            then
            echo "creating directory CD_$dirnum"
            mkdir "CD_$dirnum"
        fi
        echo "moving $i to CD_$dirnum"
        mv "$i" "CD_$dirnum"
    fi
done
person Sparr    schedule 28.12.2008
comment
Спасибо, Спарр... Я не в UNIX.... но я всегда могу поделиться папкой между Win и виртуальной машиной Unix и запустить этот скрипт. Я попробую. - person Joao Heleno; 28.12.2008
comment
bash доступен в Windows через cygwin, хотя необходимо уделить некоторое внимание таким вопросам, как буквы дисков и \ vs / - person Sparr; 29.12.2008
comment
Кроме того, как указывает ответ joel.neely, одно очевидное улучшение состоит в том, чтобы искать более мелкие вещи для перемещения в почти полный каталог вместо создания нового, как только следующий элемент не помещается в текущий ont. - person Sparr; 29.12.2008

В конечном счете, вы ищете решение проблемы с рюкзаком, которое приходит во многих формах.

Простой подход будет соответствовать следующему псевдокоду, но он не даст оптимальные решения для всех входных данных (см. статьи выше).

while (there are unallocated files) {
    create a new, empty directory
    set remaining space to 700,000,000
    while (the size of the smallest unallocated is at most (<=) the remaining space) {
        copy into the current the largest unallocated file with size at most the remaining space
        subtract that file's size from the remaining space
        remove that file from the set of unallocated files
    }
    burn the current directory
}

(Конечно, это предполагает, что ни один файл не будет иметь размер более 700 МБ. Если это возможно, обязательно удалите все такие файлы из нераспределенного списка, иначе приведенное выше приведет к бесконечному количеству пустых каталогов! ;-)

person joel.neely    schedule 28.12.2008

Если вы работаете в UNIX (включая Mac OSX), вы можете написать что-то вроде

tar cvzf allfolders.tgz ./allfolders
split allfolders.tgz -b 700m

Это создаст (сжатый) архив всех папок, а затем разделит его на куски размером 700M. Однако вам нужно будет повторно объединить все части, а затем снова извлечь их с помощью tar, если вы хотите восстановить исходный набор папок.

Если вы хотите сохранить их как отдельные папки ОС на компакт-диске, это довольно сложно (на самом деле, я думаю, что это своего рода проблема с рюкзаком, которая является NP-сложной).

person frankodwyer    schedule 27.12.2008

Есть инструменты, которые сделают это - аналогично ответу Франкодвайера, WinZip возьмет ваши 100 ГБ, заархивирует их и разделите его на «куски» любого размера, которые вы хотите, т.е. ~ 700 МБ

Вот страница функции разделения WinZip

person Andrew    schedule 27.12.2008

Я немного опоздал на вечеринку, но вот как я решил проблему:

#!/usr/bin/env bash

sourcedir="$1"
destdir_prefix="./disk_"
destdir_suffix=""
mblimit=4100
# bytelimit=$(( mblimit * 1024 * 1024 )) # MB as measured by OS (MiB)
bytelimit=$(( mblimit * 1000 * 1000 )) # MB as measured by marketeers
disk=() # empty array
dir_size=0
find "${sourcedir}" -type f |
  while read file; do

    file_size="$( stat --printf="%s" "${file}" )"
    disk_number=0
    stored=false
    while [[ "${stored}" == "false" ]]; do

      if [[ "${disk[$disk_number]}" == "" ]]; then
        disk[$disk_number]=0
      fi

      if [[ $(( disk[disk_number] + file_size )) -lt ${bytelimit} ]]; then
        dir="${destdir_prefix}${disk_number}${destdir_suffix}"
        mkdir -p "${dir}"
        filedir="$(echo ${file} | sed 's|[^/]*$||g')"
        mkdir -p "${dir}/${filedir}"
        disk[$disk_number]=$(( disk[disk_number] + file_size ))
        echo "${disk[$disk_number]} ${dir}/${file}"
        cp "${file}" "${dir}/${file}"
        stored=true
      else
        disk_number=$(( disk_number + 1 ))
      fi
    done
  done

Это создаст папки с именами disk_0, disk_1 и т. д. Для каждого файла он пытается поместить файл в disk_0, а если он не помещается, он пробует disk_1 и т. д.

person user187557    schedule 14.01.2020