Rsync 不会删除文件并提示“设备上没有剩余空间 (28)”

Rsync 不会删除文件并提示“设备上没有剩余空间 (28)”

我在 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()

相关内容