我在 macOS 10.14.6 (Mojave) 上使用rsync
(2.6.9,协议版本 29) 备份包含视频的磁盘。源容量为 999GB,可用容量为 75GB,备份容量为 1TB。命令为:
$ rsync -avxW --progress /Volumes/Video/ /Volumes/Video\ backup/
building file list ...
40989 files to consider
.DS_Store
26628 100% 0.00kB/s 0:00:00 (xfer#1, to-check=40987/40989)
.DocumentRevisions-V100/
.Spotlight-V100/
rsync: failed to set times on "/Volumes/Video backup/.Spotlight-V100": Operation not permitted (1)
.fseventsd/
iMovie Library.imovielibrary/movie/Original Media/
iMovie Library.imovielibrary/movie/Original Media/DSC_0004.mov
rsync: writefd_unbuffered failed to write 32768 bytes [sender]: Broken pipe (32)
rsync: write failed on "/Volumes/Video backup/iMovie Library.imovielibrary/movie/Original Media/DSC_0004.mov": No space left on device (28)
rsync error: error in file IO (code 11) at /BuildRoot/Library/Caches/com.apple.xbs/Sources/rsync/rsync-52.200.1/rsync/receiver.c(268) [receiver=2.6.9]
rsync: connection unexpectedly closed (76 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at /BuildRoot/Library/Caches/com.apple.xbs/Sources/rsync/rsync-52.200.1/rsync/io.c(453) [sender=2.6.9]
备份磁盘已满,因为rsync
没有删除原始磁盘中现在缺少的几个文件。我检查了该选项--delete-before
是否为默认选项,并尝试--delete
删除多余的文件,但同一个文件出现了同样的错误。
我怎样才能用 完美克隆磁盘rsync
?
答案1
由于缺乏一个巧妙的解决方案,我用 Python 编写了一个黑客程序来遍历目标并删除源中不存在的文件和目录。
确保不要意外交换源和目标,否则您将丢失原始文件。顺序相同rsync
:先是源,然后是目标。
默认情况下,代码会在删除前五条路径之前提示确认,因此您可以验证您没有删除源。它会删除目标中作为源中的符号链接的文件。
使用方式:
python3 rsync_pre_process.py /Volumes/source /Volumes/target
然后您可以运行rsync
,例如如下所示(并-a
保留符号链接):
rsync -avxW --delete --delete-before --progress /Volumes/source /Volumes/target
代码如下rsync_pre_process.py
:
#!/usr/bin/env python3
#
# Usage: python3 rsync_pre_process.py /Volumes/source /Volumes/target [5]
#
# You can then run: rsync -avxW --delete --delete-before --progress /Volumes/source /Volumes/target
#
# This code deletes files present in the target that are missing from the
# source. It circumvents one case of rsync's error "No space left on device
# (28)" even though you may run rsync with `-delete` and `--delete-before`.
#
# It prompts for confirmation to delete a certain number of files or
# directories, 5 by default, before deleting files without confirmation.
#
# It removes files in the target that are symlinks in the source.
#
# author: Miguel Morin
# copyright: public domain
# year: 2019
import os
import pathlib
import shutil
import sys
MAX_VERIFICATIONS = 5
def remove(path, verify):
"""Removes a file or directory, asking for confirmation first if requested."""
if verify:
print(path)
print("Remove this file? Press RET to proceed, C-c to abort.")
sys.stdin.read(1)
p = pathlib.Path(path)
if os.path.islink(path) or not p.is_dir():
os.remove(path)
else:
try:
# shutil.rmtree instead of os.remove() because the former works in
# iMovie folders where the latter throws an error, `PermissionError:
# [Errno 1] Operation not permitted:`
shutil.rmtree(path)
except PermissionError as e:
print("Unable to remove '%s' due to permission error" % path)
def cleanup(source, target, max_verifications):
"""Deletes all files from the target that are missing from the source, prompting
confirmations up to `max_verifications`.
"""
num_verifications = 0
for root, dirs, files in os.walk(target, topdown = False):
for name in files + dirs:
verify = num_verifications < max_verifications
target_filepath = os.path.join(root, name)
source_filepath = target_filepath.replace(target, source)
if not os.path.exists(source_filepath) or os.path.islink(source_filepath):
remove(target_filepath, verify)
num_verifications += 1
def main():
source = sys.argv[1]
target = sys.argv[2]
if not (source and target):
raise ValueError("You must supply source and target arguments. Your arguments: " + str(argv[1:]))
if 4 == len(sys.argv):
max_verifications = int(sys.argv[3])
else:
max_verifications = MAX_VERIFICATIONS
cleanup(source = source, target = target, max_verifications = max_verifications)
if "__main__" == __name__:
main()