我有一个目录树,我想将其备份到光盘上。不幸的是,它超过了任何一个磁盘的大小(大约 60GB)。我正在寻找一个脚本,可以将这棵树分成适当大小的块,并带有硬链接或诸如此类的东西(保持原始状态不变)。然后我可以将这些小型树输入到备份过程中(添加 PAR2 冗余等)。
这不是一个花哨的脚本,但看起来它可能已经完成了。建议?
(一步跨越和写入是不行的,因为我想在文件被烧毁之前做更多的事情。)
答案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"
我想我已经通过 samba 将结果共享到从中刻录光盘的 Windows 主机。如果您使用上述未更改的内容,您可能希望使用mkisofs
或其他解析符号链接的存档器。
答案4
我们不应该忘记,任务的本质确实很简单;正如 Haskell 教程中所述(围绕此任务的解决方案的工作编写,逐步完善)
现在让我们思考一下我们的程序将如何运行并用伪代码表达它:
main = Read list of directories and their sizes. Decide how to fit them on CD-Rs. Print solution.
听起来有道理吗?我是这么想的。
让我们稍微简化一下我们的生活,暂时假设我们将在程序之外的某个地方计算目录大小(例如,使用“
du -sb *
”)并从标准输入中读取此信息。
(此外,在您的问题中,您希望能够调整(编辑)生成的磁盘布局,然后使用工具来刻录它们。)
您可以重新使用(改编和重新使用)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() {
请注意,该eatFiles
函数将未来磁盘的布局准备为树,其中叶子是真实文件的符号链接。因此,您应该能够在刻录之前编辑布局,这符合您的要求。这mkisofs
实用程序有一个跟踪符号链接的选项,这确实在代码中使用我的mkiso
职能。
所提供的脚本(当然,您可以根据您的需要进行重写!)遵循最简单的想法:按照文件distribute
列出的顺序对文件(或者更准确地说,在 的情况下是包)的大小进行求和,不要不做任何重新安排。
“Haskell 漫游指南”更认真地对待优化问题,并建议尝试巧妙地重新排列文件的程序变体,以便它们更好地适合磁盘(并且需要更少的磁盘):
前期准备已经够多了。我们去收拾一些CD吧。
正如您可能已经认识到的那样,我们的问题是一个经典问题。它被称为“背包问题” (谷歌搜索一下,如果您还不知道它是什么。有超过100000个链接)。
让我们从贪心解开始......
(阅读更多内容第3章并进一步。)
其他智能工具
我还被告知 Debian 使用一个工具来制作它的发行版 CD,该工具比我的distribute
wrt 软件包集合更智能:它的结果更好,因为它关心软件包之间的依赖关系,并且会尝试制作可运行的软件包集合。第一个磁盘在依赖项下关闭,即,第一个磁盘中的任何包都不应需要另一个磁盘中的包(或者至少,我想说,应最大限度地减少此类依赖项的数量)。