![rsync:同步文件夹,但保留目标中的额外文件](https://linux22.com/image/886008/rsync%EF%BC%9A%E5%90%8C%E6%AD%A5%E6%96%87%E4%BB%B6%E5%A4%B9%EF%BC%8C%E4%BD%86%E4%BF%9D%E7%95%99%E7%9B%AE%E6%A0%87%E4%B8%AD%E7%9A%84%E9%A2%9D%E5%A4%96%E6%96%87%E4%BB%B6.png)
我开始rsync
使用它并尝试使本地系统上的两个文件夹保持同步。我有一个源文件夹,其内容随时间而变化(一些文件被添加,一些文件被更改,一些文件被删除),还有一个目标文件夹,我希望它几乎是源文件夹的镜像。所以我尝试使用 rsync,如下所示:
rsync -a --delete "${source_dir}" "${target_dir}";
这确实使目标的内容与源的内容完全相同。但是,我希望能够将一些文件添加到目标而不是源,但我不想每次执行 rsync 时都删除它们。另一方面,以前在源中同步然后被删除的文件仍应被删除。
有没有办法做到这一点而不必改变我想要排除的每个文件的命令?
更新:我应该说,我并不局限于使用 rsync。如果另一个程序可以完成这项工作,那也没问题。我只是尝试使用 rsync 来解决这个问题。
答案1
rsync
有一个名为--exclude-from
option 的选项,它允许您创建一个文件,其中包含您想要排除的任何文件的列表。您可以在需要添加新排除项或删除旧排除项时更新此文件。
如果创建排除文件,/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.txt
,common_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)
如何使用
- 将脚本复制到一个空文件中,另存为
backup_special.py
如果需要,可以更改脚本头部的详细选项:
# --- choose verbose (or not) verbose = True # ---
使用源和目标作为参数运行它:
python3 /path/to/backup_special.py <source_directory> <target_directory>
速度
我在我的网络驱动器(NAS)上一个包含大约 40,000 个文件和目录的 10 GB 目录上测试了该脚本,它几乎在与 rsync 相同的时间内完成了备份。
更新整个目录仅比 rsync 多花几秒钟,涉及 40,000 个文件,这在我看来是可以接受的,而且不足为奇,因为脚本需要将内容与上次备份进行比较。