将大目录树拆分为指定大小的块?


11

我有一个目录树,我想备份到光盘。不幸的是,它超过了任何一个磁盘的大小(大约60GB)。我正在寻找一个脚本,该脚本将使用硬链接或其他方式将此树拆分为适当大小的块(不更改原始树)。然后,我可以将这些大小不一的树放入备份过程中(添加PAR2冗余等)。

这不是一个花哨的脚本,但似乎已经完成了。有什么建议吗?

(跨一步写是不行的,因为我想在刻录文件之前做更多的事情。)


您是否考虑过聘请一个模糊的作家?
bsd 2012年

2
DVD媒体不可靠...我建议使用外部驱动器,Carbonite之类的在线备份,或者如果刻录媒体,请使用一些par2保护措施。
亚伦·D·马拉斯科

Answers:


7

存在为此目的设计的应用程序: dirsplit

它通常生活在cdrkitdirsplit包装中。

它可以创建具有链接的即用型文件夹,从而可以使用K3b或其他GUI软件轻松创建DVD。


这真的很好。在Ubuntu中,我在genisoimage软件包中找到了它。
nograpes

5

您也可以尝试使用我编写的工具fpart(经BSD许可):https : //sourceforge.net/projects/fpart/


2

我曾经为类似的目的制作了一个丑陋的脚本。这只是一个麻烦,但是当我写它的时候,我并不在乎执行时间或美观。我敢肯定会有更多相同概念的“产品化”版本,但是如果您希望获得一些想法或东西来开始黑客攻击,可以使用(在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"

我想我已经通过samba将结果共享给了从其中刻录光盘的Windows主机。如果您使用以上未更改的内容,则可能希望使用mkisofs或其他可解决符号链接的存档器。


我对您的脚本进行了一些更改,以处理文件名中的特殊字符(空格,初始破折号和点\[?*)。建议阅读:不要解析ls$ VAR vs $ {VAR} 的输出,并且不要引用或引用。请注意,我尚未测试生成的脚本。如果您不了解我的更改,请随时提出。
吉尔(Gilles)“所以

@Gilles:自2008年以来,我已经进行了大量阅读;-)进行更改以使脚本更通用。(不过,我不喜欢引入[相反的test
符号

您应该小写这些变量中的大多数。按照惯例,我们大写环境变量(PAGER,EDITOR,SHELL等)和内部shell变量。所有其他变量名称应至少包含一个小写字母。该约定避免了意外覆盖环境和内部变量。
克里斯·

2

我曾经写过一个脚本来解决类似的问题-我称它为“分发”(您可以阅读脚本的主要代码带有帮助消息的文件,也可以将其打包下载);从其描述

分发 -将软件包集合分发到多张CD上(特别适合将来与APT一起使用)

描述:`distribute'程序使完成与创建CD集以分发软件包集合有关的任务更加容易。任务包括:布置CD文件系统(将大量软件包拆分为几张光盘等),准备要由APT使用的集合(索引),创建ISO映像并记录光盘。

可以在“ distribute”的帮助下发布对最初分发的集合的定期更新。

它分几个阶段完成整个过程:在一个阶段,它使用到原始文件的符号链接创建粗盘“布局”,因此您可以进行干预并更改未来的磁盘树。

可以在脚本打印的帮助消息中(或通过查看源代码)阅读有关其用法的详细信息。

编写此文件时会考虑到更棘手的用例(将更新作为“ diff”(添加的新文件集)发布到原始记录的文件集合中),因此它包括一个额外的初始阶段,即“修复“文件集合的当前状态(为简单起见,它是通过符号链接复制原始文件集合的方式,在一个特殊的工作位置保存该集合的状态;然后,在将来的某个时间,将能够在文件集合的将来当前状态和此保存状态之间创建差异。因此,尽管您可能不需要此功能,但是您不能跳过此初始阶段AFAIR。

另外,我现在不确定(我是在几年前写的)它是否能很好地处理复杂的树,还是应该仅分割简单(一级)文件目录。(请务必查看帮助消息或源代码;稍后,我将在一段时间后再进行查找。)

与APT有关的内容是可选的,因此请不要注意,如果不需要,它可以准备供APT使用的软件包集合。

当然,如果您有兴趣,可以随时根据需要重写或提出改进建议。

(请注意,该软件包还包含未在上面链接的Git存储库中显示的代码清单中应用的其他有用的补丁!)


除其他事项外,我还介绍了其中的代码摘录,distribute该摘录解决了此处所要求的基本任务。
imz –伊万·扎哈拉里舍夫(Ivan Zakharyaschev)2011年

2

我们不应忘记任务的实质确实很简单;如关于Haskell的教程中所写(围绕该任务的解决方案的实现而编写,逐步完善)

现在让我们考虑一下我们的程序将如何运行并以伪代码表示它:

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

听起来合理吗?我是这么想的。

让我们简化一下生活,现在假设我们将在程序之外的某个地方计算目录大小(例如,使用“ du -sb *”),并从stdin中读取此信息。

(摘自《搭便车指南》《 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旅行者指南”更加认真地考虑了优化问题,并提出了程序变体,这些变体将尝试智能地重新排列文件,以使它们更好地适合磁盘(并且需要更少的磁盘):

预赛已经足够了。让我们打包一些CD。

您可能已经意识到,我们的问题是经典的。这被称为“背包问题” (如果您还不知道它是什么,可以用谷歌搜索一下。有超过100000个链接)。

让我们从贪婪的解决方案开始...

(在第3章中有更多了解,并继续。)

其他智能工具

有人还告诉我Debian使用一种工具来制作发行CD,比我的distributewrt软件包集合更智能:其结果更好,因为它关心软件包间的依赖关系,并会尝试使软件包的集合得以继续。在依赖关系下关闭的第一个磁盘,即,第一个磁盘上的任何软件包都不应要求另一个磁盘上的软件包(或者至少,我要说这样的依赖关系的数量应最小化)。


1

backup2l可以完成很多工作。即使您不直接使用该程序包,也可能会从中获得一些脚本提示。


0

rar归档可以指示自动分割它创建了与特定大小的块存档-vsize标志。

将该目录树归档为foo您指定的每个块(例如每个块500 MB)
rar a backup.rar -v500m foo/


2
比为什么rar?tar(+ bz2)+ split是* nix的更原始方法。
rvs

“一口大小的树”听起来并不像rar,除非您再次将每个“零件”解压缩到其自己的目录中,这当然是行不通的,因为零件不是那样设计的,并且不会在文件边界上拆分。
MattBianco 2011年

1
如果谈论给出类似tar+ split结果的工具,那么还有dar ; 这是有关其相关功能的注释:“(SLICES)设计为能够将归档分割成多个可移动介质,而无论其数量和大小如何。” 我认为,与tar+ 相比split,它提供了一些更简便的方法来访问存档文件。(顺便说一句,它也具有类似的功能distribute:“差异备份”和“目录树快照”,但是可能不喜欢结果是一种特殊格式,而不是带有目录树的ISO。)
imz – Ivan Zakharyaschev
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.