大きなディレクトリツリーを指定されたサイズのチャンクに分割しますか?

大きなディレクトリツリーを指定されたサイズのチャンクに分割しますか?

ディスクにバックアップしたいディレクトリツリーがあります。残念ながら、両方のディスクのサイズ(約60 GB)を超えています。私はこのツリーをハードリンクなどを使って適切なサイズの塊に分割することができるスクリプトを探しています(元のまま)。その後、これらの小さなツリーをバックアッププロセスに供給できます(PAR2冗長性の追加など)。

派手な台本ではありませんが、おそらく完成したようです。提案?

(ファイルが書き込まれる前にさらに作業をしたいので、1段階でスパニングと書き込みは機能しません。)

答え1

そのために設計されたアプリケーションがあります。dirsplit

cdrkit通常、包装または包装に見られますdirsplit

K3bまたは他のGUIソフトウェアを使用してDVDを簡単に作成できるリンクを含むすぐに利用可能なフォルダを作成します。

答え2

また試すことができます部分、私が書いたツール(BSDライセンス): https://sourceforge.net/projects/fpart/

答え3

私は同様の目的で醜いスクリプトを作成したことがあります。ただパッチワークに過ぎませんが、私が書いたときは実行時間や審美的な側面を考慮しませんでした。同じコンセプトの「プロダクション準備」バージョンがさらにあると確信していますが、ハッキングを開始できるアイデアや何かを入手したい場合は、以下を参照してください(2008年に制作されたので、リスク負担は本人が監修して使用してください!)。 -)

#!/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"

ディスクを焼いたWindowsホストでSambaを介して結果を共有したようです。上記を変更せずに使用する場合、またはシンボリックリンクを解決する他のアーカイバを使用できますmkisofs

答え4

私たちは、Haskellチュートリアルで説明されているように、タスクの性質が実際に単純であることを忘れてはなりません(このタスクのソリューションタスクを中心に作成され、徐々に改善されています)。

では、プログラムがどのように実行され、擬似コードで表現されるかを考えてみましょう。

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

合理的に聞こえますか?私はそう思った。

du -sb *私たちの人生を少し単純化し、プログラムの外のどこかで(たとえば、 ""を使って)ディレクトリサイズを計算し、標準入力からこの情報を読むとしましょう。

(からHaskellのヒッチハイキングガイド、第1章)

(また、あなたの質問に従って結果のディスクレイアウトを調整(編集)してから、ツールを使って焼くことができるようにしたいです。)

Haskellチュートリアルの簡単なプログラムバリアントを再利用(適応および再利用)して、ファイルのコレクションを分割できます。

残念ながら、distribute他の回答で述べたツール基本的には、タスク分割の単純さはユーザーインターフェイスの複雑さや膨らみと一致しませんdistribute(複数のタスクを組み合わせるように書かれているため、段階的に実行している間に今考えることができる最もきれいな方法ではありません)。 。

これを最大限に活用するために、以下はbashコードの一部ですdistribute(場所:380ライン)は、ファイルのコレクションを分割する「基本」操作を実行するために使用されます。

# 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() {

454行目以降をよく読んでください。)

このeatFiles機能は将来のディスクのレイアウトをツリーとして準備し、リーフは実際のファイルへのシンボリックリンクです。したがって、書き込み前に要件に合わせてレイアウトを編集できる必要があります。このmkisofsユーティリティには、コードで実際に使用されているシンボリックリンクを追跡するオプションがあります。私のmkiso機能

提供されたスクリプト(もちろん必要に応じて上書きすることができます!)は最も簡単なアイデアに従います。distributeつまり、リストされた順序でファイルサイズ(より正確にはパッケージの場合)を探します。いかなる再配列もしないでください。

「Haskellを使用したHichheikerのためのガイド」では、最適化の問題をより深く扱い、ファイルがディスクに合うように(そしてより少ないディスクを必要とするように)、ファイルを再配置するプログラムのバリエーションをお勧めします。

事前準備は十分です。 CDを少し手に入れよう。

あなたが知っているように、私たちの問題は古典的な問題です。知られている「バックパックの問題」Googleを試してください。、まだそれが何であるかわからない場合。リンク数は100,000を超えます。)

貪欲なソリューションから始めましょう。

(もっと読む第3章そしてもっと。 )

その他のスマートツール

また、Debianは私のwrtパッケージコレクションよりもスマートな配布CDを作成するためにツールを使用していると聞きましたdistribute。その結果は、パッケージ間の依存関係を考慮し、実行するパッケージのコレクションを作成しようとするので、より良いです。最初のディスクは依存関係に従って閉じられます。つまり、最初のディスクのどのパッケージも他のディスクのパッケージを要求してはなりません(または少なくともそのような依存関係の数は最小限に抑える必要があると言いたいです)。

関連情報