使用备份工具时duplicity
,我注意到如果我重命名源上的文件,数据将通过网络再次重新发送到目标,这有点悲伤。由于内部duplicity
使用librsync
,我决定看看rsync
.
这建筑维基页面状态:
处理重命名
移动/重命名的文件会被检测到,并且不会存储或传输两次。它通常意味着计算文件或其块的校验和。缺少此功能的应用程序可以通过结合来补充hsync
AUR,仅同步重命名。
rsync:处理重命名:否
这是否真的意味着,使用时rsync
,无法阻止 10 GB 通过网络重新传输到目的地,如果我在源计算机上重命名/test/10GBfile
为?/test/10GBfile_newname
鉴于 的长期流行rsync
,是否有更好的处理方式?
答案1
没有rsync
跟踪重命名的机制,因为它除了运行时之外不维护状态。如果您在源计算机上重命名/test/10GBfile
为,则默认情况下只能看到已删除的和已创建的。/test/10GBfile_newname
rsync
10GBfile
10GBfile_newname
参数--fuzzy
( -y
) 可能有助于识别目标上10GBfile
的潜在数据源10GBfile_newname
,从而避免以文件复制为代价进行网络复制。但是,它(主要)只能考虑文件的匹配在同一目录下因此,尽管您的示例可以匹配,但重命名为/test/10GBfile
to/test/otherdir/10GBfile_newname
却不会。
另请注意,文档 ( man rsync
) 建议,如果您想使用,--delete
则应使用--delay-updates
或 ,--delete-after
以便--fuzzy
在使用之前不会删除 的潜在匹配项。
例子
# Prepare an uncompressible 100MB file
mkdir -p /tmp/test
dd bs=1M count=100 iflag=fullblock if=/dev/urandom >/tmp/test/file1
# Normal first-time copy
rsync -av --fuzzy --delete-after /tmp/test/ remote:/tmp/test
# Skip copy because unchanged
rsync -av --fuzzy --delete-after /tmp/test/ remote:/tmp/test
# Rename file (per your example)
mv /tmp/test/file1 /tmp/test/file2
# Fast copy because fuzzy match
rsync -av --fuzzy --delete-after /tmp/test/ remote:/tmp/test
添加另外两个-v
标志(即rsync -avvv …
)以查看正在发生的情况的逐块详细信息。
答案2
--fuzzy
已经得到了回答,但还有另一个涉及硬链接的有趣黑客。
第一次转账后
$ rsync -avHP --delete-after ~/family/Photos remotebox:backups
您创建一个硬链接工作目录:
$ cd ~/family
$ cp -rlp Photos Photos-work
然后你可以使用
$ rsync -avHP --delete-after --no-inc-recursive ~/family/Photos ~/family/Photos-work remotebox:backups
将新结构传输到远程。
这里解释了为什么以及如何工作:
https://lincolnloop.com/blog/detecting-file-moves-renames-rsync/
答案3
那一页声称选项补丁--detect-renamed
可用于rsync 3.0.9
.它有补丁的链接,链接到bugzilla 讨论。我的rsync 3.1.3
没有--detect-renamed
选择。所提到的讨论正在进行中:
2021-01-15 14:19:12 UTC 时间
此功能请求太旧了,已经失去了相关性,因为 btrfs/zfs/etc 是比 rsync 更优化的备份解决方案。
有些人不同意“失去相关性”,我同意那些不同意的人的观点。
那红迪网页面索赔:
btrfs 同步支持此功能,但您需要两端都有 btrfs。
我认为是什么埃拉特拉特提到的。
以下是我现在的解决方法(git
我认为对我来说有点过分了)。
我的具体情况:我不经常重命名,我想要同步链接(不仅仅是一对一),而不是备份。因此,我决定创建 shell 命令文件,在其中编写mv
命令(在末尾添加新命令),以便由我最初编写的来回运行 rsync 的脚本额外执行。该脚本尚未经过太多测试,运行风险自负,欢迎提出改进意见。我希望 rsync--detect-renamed
选项能够尽快投入生产。
接下来的完整脚本很长,因为当同步一侧的 shell 文件大小大于另一侧的 shell 文件时,它会单独处理,并且包括一些检查,因为我不确定(最近才开始使用该脚本)comm
程序是否正确找到了唯一的行,因为它据称适用于“排序”(每个手册页)文件。核心是:
# do renames / moves at both ends
cd $remote_path
bash -c "$(comm --nocheck-order -3 -1 $f_RE $f_LO)"
cd $local_path
bash -c "$(comm --nocheck-order -3 -2 $f_RE $f_LO)"
# merge files via temp file
(comm --nocheck-order -1 -2 $f_LO $f_RE) > $f_LO.3
(comm --nocheck-order -3 -1 $f_RE $f_LO) >> $f_LO.3
(comm --nocheck-order -3 -2 $f_RE $f_LO) >> $f_LO.3
cp $f_LO.3 $f_RE
mv $f_LO.3 --force $f_LO
rm $f_LO.1 $f_LO.2
完整的外壳功能:
#!/bin/bash
do_sync(){
if [ -d $remote_path ]; then
# ===== workaround for renaming / moving (run manually made commands before rsync) ===== #
# man comm: comm - compare two sorted files line by line
complex_flag=0 # later set by script to 1 if changes identified from both sync directions
to_rsync=1 # to run rsync by default
f_RE=$remote_path/_rename_move.sh
f_LO=$local_path/_rename_move.sh
if [ -f $f_RE ]; then
if [ -f $f_LO ]; then
# if both exist, -gt greated than, stat --printf="%s" size in bytes
if [ $(stat --printf="%s" $f_RE) -gt $(stat --printf="%s" $f_LO) ]; then
# small file (2nd) is fully contained in the beginning of larger file (maybe test binary mode more efficient)
# -1 suppress column 1 (lines unique to FILE1) : man comm
if [ -z "$(comm --nocheck-order -3 -1 $f_RE $f_LO)" ]; then
# run only additional commands
cd $local_path
bash -c "$(comm --nocheck-order -3 -2 $f_RE $f_LO)"
# overwrite small with larger one
cp $f_RE $f_LO
else complex_flag=1; fi
# remote smaller than local
elif [ $(stat --printf="%s" $f_RE) -lt $(stat --printf="%s" $f_LO) ]; then
# small file (1nd) is fully contained in the beginning of larger file (maybe test binary mode more efficient)
if [ -z "$(comm --nocheck-order -3 -2 $f_RE $f_LO)" ]; then
# run only additional commands
cd $remote_path
bash -c "$(comm --nocheck-order -3 -1 $f_RE $f_LO)"
# overwrite small with larger one
cp $f_LO $f_RE
else complex_flag=1; fi
# same size but different contents
elif [ ! $(sha256sum $f_RE | awk '{ print $1 }') = $(sha256sum $f_LO | awk '{ print $1 }') ]; then
complex_flag=1;
fi
# nothing to do if files are the same
# if only remote exists
else
cd $local_path && $f_RE
fi
# neither file was found to be part of another as a whole
# expect changes (moves/renames) from both ends
if [ $complex_flag -eq 1 ]; then
# doing echo "$()" removes trailing empty lines compared to for some reason (TODO why?)
# check that doing symmetrically with appending to local results in same number of lines in a file
# and selecting matching in both and adding distinct from both too results in same number of lines in a file
cp $f_RE $f_LO.1 && (comm --nocheck-order -3 -1 $f_RE $f_LO) >> $f_LO.1
cp $f_LO $f_LO.2 && (comm --nocheck-order -3 -2 $f_RE $f_LO) >> $f_LO.2
(comm --nocheck-order -1 -2 $f_LO $f_RE) > $f_LO.3
(comm --nocheck-order -3 -1 $f_RE $f_LO) >> $f_LO.3
(comm --nocheck-order -3 -2 $f_RE $f_LO) >> $f_LO.3
counts_1="$(wc $f_LO.1 | awk '{ print $1,$2,$3 }')"
counts_2="$(wc $f_LO.2 | awk '{ print $1,$2,$3 }')"
counts_3="$(wc $f_LO.3 | awk '{ print $1,$2,$3 }')"
# same counts, Ok
if [ $counts_1 = $counts_2 ] && [ $counts_2 = $counts_3 ]; then
cd $remote_path
bash -c "$(comm --nocheck-order -3 -1 $f_RE $f_LO)"
cd $local_path
bash -c "$(comm --nocheck-order -3 -2 $f_RE $f_LO)"
cp $f_LO.3 $f_RE
mv $f_LO.3 --force $f_LO
rm $f_LO.1 $f_LO.2
else
echo "========= manual intervention might be needed ==========="
echo "Results of analysis of $f_LO & $f_RE via [comm] app has not matched;"
echo "renaming/moving not performed;"
echo "rsync of $local_path & $remote_path not performed; see differences between files:"
echo "$f_LO.1, $f_LO.2, $f_LO.3"
echo "========================================================="
to_rsync=0
fi
fi
# if only local exists
elif [ -f $f_LO ]; then
cd $remote_path && $f_LO
fi
# ===== end of workaround ===== #
if [ $to_rsync -eq 1 ];then
rsync $options $local_path/ $remote_path
rsync $options $remote_path/ $local_path
rsync $options $local_path/ $remote_path
fi
# below is to move old versions away
find "$local_path" -path "$local_path/prevs" -prune -o -name '*.bak' -exec mv "{}" "$local_path/prevs" \;
find "$remote_path" -path "$remote_path/prevs" -prune -o -name '*.bak' -exec mv "{}" "$remote_path/prevs" \;
# previous versions, prune works "more correct", forgot why
# find $local_path -maxdepth 1 -name '*.bak' -exec mv "{}" $local_path/prevs \;
# find $remote_path -maxdepth 1 -name '*.bak' -exec mv "{}" $remote_path/prevs \;
else
echo $remote_path is not available
fi
}
# trailing / would prevent proper pruning in find commands
local_path=/home/$(id -un)/Documents
remote_path=/media/$(id -un)/Projects
do_sync
答案在https://serverfault.com/questions/489289/handling-renamed-files-or-directories-in-rsync据我了解,它有一些缺点。
带有硬链接的:大多数系统不支持目录的硬链接,并且不会检查哪一侧已更改名称(可以进行备份,不能进行同步)。
stat filename
可以用来检查哪一侧的文件已重命名,但我不知道如何将其与 rsync 合并。