Dividir a árvore de diretórios grande em partes de tamanho especificado?

9

Eu tenho uma árvore de diretórios que gostaria de fazer backup em discos óticos. Infelizmente, ele excede o tamanho de qualquer disco (cerca de 60 GB). Eu estou procurando um script que dividiria essa árvore em pedaços de tamanho apropriado com links rígidos ou outros enfeites (deixando o original intocado). Eu poderia alimentar essas árvores pequenas no processo de backup (adicionar redundância de PAR2, etc.).

Não é um script sofisticado, mas parece que já foi feito. Sugestões?

(Abordar e escrever em um passo é inútil porque eu quero fazer mais coisas antes que os arquivos sejam queimados.)

    
por Reid 26.03.2011 / 20:14

7 respostas

5

Você também pode tentar o fpart , uma ferramenta que eu escrevi (licenciado pelo BSD): link

    
por 07.12.2011 / 12:43
4

Existe uma aplicação projetada para isso: dirsplit

Geralmente, ele vive em cdrkit ou dirsplit packages.

Ele pode criar pastas prontas para uso com links para criar DVDs facilmente com o K3b ou outro software de GUI

    
por 05.07.2011 / 22:26
2

Eu fiz uma vez um roteiro feio para um propósito similar. É apenas um kludge, mas quando eu escrevi não me importei com o tempo de execução ou a beleza. Tenho certeza de que existem mais versões "productificadas" do mesmo conceito, mas se você deseja obter algumas idéias ou algo para começar a usar, aqui vai (fez isso em 2008, então use a seu próprio risco!): - )

#!/bin/sh -
REPO=/export/foton/PictureStore
LINKS=/export/foton/links
SPLITTIX='date '+%y%m%d-%H%M''

# kilobytes
DVDSIZE=4400000
PARTPREFIX="DVD-"
REPOSIZE='du -sk -- ${REPO} | awk '{print $1}''
NUMPARTS='expr $REPOSIZE / $DVDSIZE'
SPLITDIR=${LINKS}/splits/${SPLITTIX}
mkdir -p -- "$SPLITDIR"

PARTNUM=1
PARTSIZ=0
DONESIZ=0
PARTNUM='echo $PARTNUM | awk '{printf("%03x", $0)}''
mkdir -p -- "${SPLITDIR}/${PARTPREFIX}${PARTNUM}"
for D in "${REPO}"/..?* "${REPO}"/.[!.]* "${REPO}"/*
do
  if [ ! -e "$D" ]; then continue; fi  # skip ..?*, .[!.]* and * if there are no matching files
  D=${D#$REPO/}
  D_SIZ='du -sk -- "${REPO}/$D" | awk '{print $1}''
  if test 'expr $D_SIZ + $PARTSIZ' -le $DVDSIZE
  then
    # link to D in this part
    ln -s -- "$REPO/$D" "${SPLITDIR}/${PARTPREFIX}${PARTNUM}/$D"
    # adjust counters
    PARTSIZ='expr $PARTSIZ + $D_SIZ'
    DONESIZ='expr $DONESIZ + $D_SIZ'
  else
    # next part and link to D in that
    echo PART $PARTNUM: $PARTSIZ kb '(target' $DVDSIZE 'kb)'
    PARTNUM='expr $PARTNUM + 1'
    PARTNUM='echo $PARTNUM | awk '{printf("%03x", $0)}''
    PARTSIZ=$D_SIZ
    DONESIZ='expr $DONESIZ + $D_SIZ'
    mkdir -p -- "${SPLITDIR}/${PARTPREFIX}${PARTNUM}"
    ln -s -- "$REPO/$D" "${SPLITDIR}/${PARTPREFIX}${PARTNUM}/$D"
  fi
done
echo "wrote $DONESIZ kb in $PARTNUM parts in $SPLITDIR"

Eu acho que tive o resultado compartilhado através do samba para um host do Windows que gravou discos dele. Se você usar o acima inalterado, você pode querer usar mkisofs ou outro arquivador que resolve links simbólicos.

    
por 28.03.2011 / 09:49
2

Uma vez eu escrevi um script para resolver um problema semelhante - eu o chamei de "distribuir" (você pode ler o código principal do script ou o arquivo com a mensagem de ajuda , ou faça o download como um pacote ); da sua descrição :

distribute -- Distribute a collection of packages on multiple CDs (especially good for future use with APT)

Description: 'distribute' program makes doing the tasks related to creating a CD set for distribution of a collection of packages easier. The tasks include: laying out the CDs filesystem (splitting the large amount of packages into several discs etc.), preparing the collection for use by APT (indexing), creating ISO images and recording the discs.

Periodical updates to the initially distributed collection can be issued with help of 'distribute'.

Ele faz todo o processo em vários estágios: em um estágio, ele cria os "layouts" do disco fururio usando links simbólicos para os arquivos originais - assim você pode intervir e mudar as futuras árvores de disco.

Os detalhes sobre seu uso podem ser lidos na mensagem de ajuda impressa pelo script (ou pelo código-fonte).

Foi escrito com um caso de uso mais complicado em mente (emitindo atualizações como um "diff" - o conjunto de novos arquivos adicionados - para a coleção de arquivos originalmente gravada), então ele inclui um estágio inicial extra, , "consertando" o estado atual da coleção de arquivos (para simplificar, faz isto replicando a coleção original de arquivos por meio de links simbólicos, em um lugar de funcionamento especial por salvar os estados da coleção; então, algum tempo no No futuro, ele poderá criar um diff entre um futuro estado atual da coleção de arquivos e esse estado salvo). Então, embora você possa não precisar desse recurso, não é possível pular este estágio inicial, AFAIR.

Além disso, não tenho certeza agora (escrevi há alguns anos) se ele trata árvores complexas bem, ou se deve dividir apenas diretórios simples (um nível) de arquivos. (Por favor, olhe para a mensagem de ajuda ou o código-fonte para ter certeza; eu vou procurar isso também, um pouco mais tarde, quando eu tiver algum tempo.)

O material relacionado ao APT é opcional, por isso não preste atenção para que ele possa preparar coleções de pacotes a serem usadas pelo APT se você não precisar disso.

Se você se interessar, é claro, sinta-se à vontade para reescrevê-lo para suas necessidades ou sugerir melhorias.

(Por favor, preste atenção que o pacote inclui patches úteis adicionais não aplicados no código apresentado listando no repositório do Git ligado acima!)

    
por 28.03.2011 / 11:28
2

Não devemos esquecer que a essência da tarefa é de fato bastante simples; como colocado em um tutorial sobre Haskell (que é escrito em torno do trabalho através da solução para esta tarefa, incrementalmente refinado)

Now let's think for a moment about how our program will operate and express it in pseudocode:

main = Read list of directories and their sizes.
       Decide how to fit them on CD-Rs.
       Print solution.

Sounds reasonable? I thought so.

Let's simplify our life a little and assume for now that we will compute directory sizes somewhere outside our program (for example, with "du -sb *") and read this information from stdin.

(de Guia do Hitchhikers para Haskell, Capítulo 1 )

(Além disso, na sua pergunta, você gostaria de poder ajustar (editar) os layouts de disco resultantes e, em seguida, usar uma ferramenta para gravá-los.)

Você pode reutilizar (adaptar e reutilizar) uma simples variante do programa desse tutorial do Haskell para dividir sua coleção de arquivos.

Infelizmente, em o distribute ferramenta que eu mencionei aqui em outra resposta , a simplicidade da tarefa de divisão essencial não é correspondida pela complexidade e inchaço da interface do usuário de distribute (porque foi escrita para combinar várias tarefas; embora executada em etapas, mas ainda combinadas não da maneira mais limpa que eu poderia pensar agora).

Para ajudar você a usar o código, aqui está um trecho do bash-code de distribute (em linha 380 ) que serve para fazer essa tarefa" essencial "de dividir um coleção de arquivos:

# Splitting:

function splitMirrorDir() {
  if [[ ! -d "$THIS_BASES_DIR/$BASE/$type" ]]; then
    echo $"No base fixed for $type" >&2
    exit 1
  fi

  # Getting the list of all suitable files:
  local -a allFiles
  let 'no = 0' ||:
  allFiles=()
  # no points to the next free position in allFiles
  # allFiles contains the constructed list
  for p in "$THIS_BASES_DIR/$BASE/$type"/*.rpm; do
      if [[ ! -e "$p" ]]; then
      # fail on non-existent files
      echo $"Package file doesn't exist: " "$p" >&2
      return 1 
      fi
      if [[ "$ONLY_REAL_FILES" == "yes" && ! -f "$p" ]]; then
      continue
      fi
      if [[ "$DIFF_TO_BASE" ]]; then
          older_copy="$DIFF_TO_BASE/$type/${p##*/}" # using shell param expansion instead of 'basename' to speed up
          if [[ -h "$older_copy" || -a "$older_copy" ]]; then
          continue
      fi
      fi
      allFiles[$(( no++ ))]="$p"
  done
  readonly -a allFiles

  # Splitting the list of all files into future disks:
  # 
  local -a filesToEat allSizes
  let 'no = 0' ||:
  filesToEat=()
  allSizes=($(getSize "${allFiles[@]}"))
  readonly -a allSizes
  # allSizes contains the sizes corrsponding to allFiles
  # filesToEat hold the constructed list of files to put on the current disk
  # no points to the next free position in filesToEat
  # totalSize should hold the sum of the sizes 
  #  of the files already put into filesToEat;
  #  it is set and reset externally.
  for p in "${allFiles[@]}"; do 
      if (( totalsize + ${allSizes[$(( no ))]} > CDVOLUME )); then
      eatFiles "${filesToEat[@]}"
          filesToEat=()
          finishCD
      startTypedCD
    fi
      let "totalsize += ${allSizes[$(( no ))]}" ||:
      filesToEat[$(( no++ ))]="$p"
  done
  eatFiles "${filesToEat[@]}"
}

function eatFiles() {
    #{ oldIFS="$IFS"; IFS=$'\n'; echo "$FUNCNAME: args: " "$*" | head >&2;  IFS="$oldIFS"; }
    zeroDelimited "$@" | xargs -0 --no-run-if-empty \
    cp -s \
    --target-dir="$THIS_LAYOUTS_DIR/cd$(( cdN ))/$PREFIX/$type$DOT_SUFFIX"/ \
    --
}

function startTypedCD() {
#  set -x
  mkdir -p "$THIS_LAYOUTS_DIR/cd$(( cdN ))/$PREFIX/$type$DOT_SUFFIX"
  start_action $" %s with %s" "$(( cdN ))" "$type"
#  set +x
}

function finishCD() {

( leia mais após a linha 454 )

Observe que a função eatFiles prepara os layouts dos futuros discos como árvores nas quais as folhas são links simbólicos para os arquivos reais. Portanto, é atender a sua exigência de que você possa editar os layouts antes de gravar. O utilitário mkisofs tem uma opção para seguir links simbólicos, que é de fato empregado no código de minha função mkiso .

O script apresentado (o que você pode fazer e reescrever para as suas necessidades, é claro!) segue a idéia mais simples: somar os tamanhos dos arquivos (ou, mais precisamente, pacotes no caso de distribute ) apenas no ordem que foram listados, não faça qualquer rearranjo.

O guia "Hitchhikers to Haskell" leva o problema de otimização mais a sério e sugere variantes de programa que tentariam reorganizar os arquivos de maneira inteligente, para que eles se ajustassem melhor aos discos (e exigissem menos discos):

Enough preliminaries already. let's go pack some CDs.

As you might already have recognized, our problem is a classical one. It is called a "knapsack problem" (google it up, if you don't know already what it is. There are more than 100000 links).

let's start from the greedy solution...

(leia mais em Capítulo 3 e mais adiante)

Outras ferramentas inteligentes

Foi-me dito também que o Debian usa uma ferramenta para fazer seus CDs de distro mais inteligentes que meu distribute w.r.t. coleções de pacotes: seus resultados são melhores porque se preocupam com dependências entre pacotes e tentam fazer com que a coleção de pacotes que fica no primeiro disco seja fechada sob dependências, ou seja, nenhum pacote do primeiro disco deve requerer um pacote de outro disco (ou pelo menos, eu diria, o número de tais dependências deve ser minimizado).

    
por 30.03.2011 / 19:11
1

backup2l pode fazer muito desse trabalho. Mesmo que você não use o pacote diretamente, você pode obter algumas ideias de script dele.

    
por 29.03.2011 / 01:50
0

O rar archiver pode ser instruído para dividir automaticamente o arquivo criado em partes de um tamanho específico com o sinal -vsize .

Arquivando essa árvore de diretórios denominada foo em partes de, digamos, 500 megabytes cada, você pode especificar rar a backup.rar -v500m foo/

    
por 26.03.2011 / 21:40