rsync:同步文件夹,但保留目标中的额外文件

rsync:同步文件夹,但保留目标中的额外文件

我开始rsync使用它并尝试使本地系统上的两个文件夹保持同步。我有一个源文件夹,其内容随时间而变化(一些文件被添加,一些文件被更改,一些文件被删除),还有一个目标文件夹,我希望它几乎是源文件夹的镜像。所以我尝试使用 rsync,如下所示:

rsync -a --delete "${source_dir}" "${target_dir}";

这确实使目标的内容与源的内容完全相同。但是,我希望能够将一些文件添加到目标而不是源,但我不想每次执行 rsync 时都删除它们。另一方面,以前在源中同步然后被删除的文件仍应被删除。

有没有办法做到这一点而不必改变我想要排除的每个文件的命令?

更新:我应该说,我并不局限于使用 rsync。如果另一个程序可以完成这项工作,那也没问题。我只是尝试使用 rsync 来解决这个问题。

答案1

rsync有一个名为--exclude-fromoption 的选项,它允许您创建一个文件,其中包含您想要排除的任何文件的列表。您可以在需要添加新排除项或删除旧排除项时更新此文件。

如果创建排除文件,/home/user/rsync_exclude新命令将是:

rsync -a --delete --exclude-from="/home/user/rsync_exclude" "${source_dir}" "${target_dir}"

创建排除列表文件时,应将每条排除规则放在单独的行上。排除项与源目录相关。如果您的/home/user/rsync_exclude文件包含以下选项:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • 您的源目录中调用的任何文件或目录secret_file都将被排除。
  • 中的任何文件都将被排除,但将同步${source_dir}/first_dir/subdir的空版本。subdir
  • ${source_dir}/second_dir中带有 前缀的任何文件common_name.都将被忽略。所以common_name.txtcommon_name.jpg等等。

答案2

既然你提到:我不仅限于 rsync:

维护镜像的脚本,允许向目标添加额外的文件

下面的脚本完全按照您的描述执行。

该脚本可以在冗长模式(在脚本中设置),它将输出备份(镜像)的进度。不用说,这也可以用来记录备份:

详细选项

在此处输入图片描述


这个概念

1. 第一次备份时,脚本:

  • 创建一个文件(在目标目录中),其中列出所有文件和目录;.recentfiles
  • 创建目标目录中所有文件和目录的精确副本(镜像)

2. 接下来依次类推备份

  • 该脚本比较文件的目录结构和修改日期。源中的新文件和目录被复制到镜像中。同时创建第二个(临时)文件,列出源目录中的当前文件和目录;.currentfiles
  • 随后,.recentfiles将(列出上次备份的情况)与进行比较.currentfiles仅有的.recentfiles不存在的文件.currentfiles显然会从源中删除,并且也会从目标中删除。
  • 您手动添加到目标文件夹的文件不会被脚本以任何方式“看到”,并且会被保留下来。
  • 最后,将临时文件.currentfiles重命名为,.recentfiles以服务于下一个备份周期,依此类推。

剧本

#!/usr/bin/env python3
import os
import sys
import shutil

dr1 = sys.argv[1]; dr2 = sys.argv[2]

# --- choose verbose (or not)
verbose = True
# ---

recentfiles = os.path.join(dr2, ".recentfiles")
currentfiles = os.path.join(dr2, ".currentfiles")

if verbose:
    print("Counting items in source...")
    file_count = sum([len(files)+len(d) for r, d, files in os.walk(dr1)])
    print(file_count, "items in source")
    print("Reading directory & file structure...")
    done = 0; chunk = int(file_count/5); full = chunk*5

def show_percentage(done):
    if done % chunk == 0:
        print(str(int(done/full*100))+"%...", end = " ")

for root, dirs, files in os.walk(dr1):
    for dr in dirs:
        if verbose:
            if done == 0:
                print("Updating mirror...")
            done = done + 1
            show_percentage(done) 
        target = os.path.join(root, dr).replace(dr1, dr2)
        source = os.path.join(root, dr)
        open(currentfiles, "a+").write(target+"\n")
        if not os.path.exists(target):
            shutil.copytree(source, target)
    for f in files:
        if verbose:
            done = done + 1
            show_percentage(done)
        target = os.path.join(root, f).replace(dr1, dr2)
        source = os.path.join(root, f)
        open(currentfiles, "a+").write(target+"\n") 
        sourcedit = os.path.getmtime(source)
        try:
            if os.path.getmtime(source) > os.path.getmtime(target):
                shutil.copy(source, target)   
        except FileNotFoundError:
            shutil.copy(source, target)

if verbose:
    print("\nChecking for deleted files in source...")

if os.path.exists(recentfiles):
    recent = [f.strip() for f in open(recentfiles).readlines()]
    current = [f.strip() for f in open(currentfiles).readlines()]
    remove = set([f for f in recent if not f in current])
    for f in remove:
        try:
            os.remove(f)
        except IsADirectoryError:
            shutil.rmtree(f)
        except FileNotFoundError:     
            pass
        if verbose:
            print("Removed:", f.split("/")[-1])

if verbose:
    print("Done.")

shutil.move(currentfiles, recentfiles)

如何使用

  1. 将脚本复制到一个空文件中,另存为backup_special.py
  2. 如果需要,可以更改脚本头部的详细选项:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. 使用源和目标作为参数运行它:

     python3 /path/to/backup_special.py <source_directory> <target_directory>
    

速度

我在我的网络驱动器(NAS)上一个包含大约 40,000 个文件和目录的 10 GB 目录上测试了该脚本,它几乎在与 rsync 相同的时间内完成了备份。

更新整个目录仅比 rsync 多花几秒钟,涉及 40,000 个文件,这在我看来是可以接受的,而且不足为奇,因为脚本需要将内容与上次备份进行比较。

相关内容