Contents

双向同步Linux目录

1. 概述

通常,我们需要将文件或目录备份到本地机器上的另一个位置,或者本地和远程机器之间。我们可以通过复制和粘贴的方式手动完成,这既费时又容易出错,或者我们可以使用一些命令或程序来完成。

在本教程中,我们将测试几个命令或程序以在一台机器上双向同步目录并比较结果。

我们使用 64 位*rsync * 3.1.3、64位Unison 2.51.4 和用于 Debian、Zaloha2 的 OCaml 4.05.0 二进制文件以及在 64 位 Debian 10.10 (Buster) 和 GNU bash 上运行的 Linux 的FreeFileSync 11.16 二进制文件测试了代码5.0.3。

2. 使用rsync命令

2.1.安装

rsync常见于类 Unix 操作系统上。它是一个基于 GNU GPL 的开源项目,它的源代码或二进制文件可以从它的主页 上下载。

2.2. 用法

rsync在一个方向上工作,所以我们需要运行两次才能在两个方向上同步目录

让我们试一试。首先,我们来看看源目录和目标目录的结构:

$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   └── file03.bin
└── target
    ├── file0A.bin
    ├── file0B.bin
    └── file0C.bin
2 directories, 6 files

现在,让我们从源同步到目标:

$ rsync -Pcauv source/ target/
sending incremental file list
./
file01.bin
              0 100%    0.00kB/s    0:00:00 (xfr#1, to-chk=2/4)
file02.bin
              0 100%    0.00kB/s    0:00:00 (xfr#2, to-chk=1/4)
file03.bin
              0 100%    0.00kB/s    0:00:00 (xfr#3, to-chk=0/4)
sent 297 bytes  received 76 bytes  746.00 bytes/sec
total size is 0  speedup is 0.00

并检查我们的中间结果:

$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   └── file03.bin
└── target
    ├── file01.bin
    ├── file02.bin
    ├── file03.bin
    ├── file0A.bin
    ├── file0B.bin
    └── file0C.bin
2 directories, 9 files

然后,我们从目标同步到源:

$ rsync -Pcauv target/ source/
sending incremental file list
file0A.bin
              0 100%    0.00kB/s    0:00:00 (xfr#1, to-chk=2/7)
file0B.bin
              0 100%    0.00kB/s    0:00:00 (xfr#2, to-chk=1/7)
file0C.bin
              0 100%    0.00kB/s    0:00:00 (xfr#3, to-chk=0/7)
sent 401 bytes  received 73 bytes  948.00 bytes/sec
total size is 0  speedup is 0.00

现在,让我们检查一下我们的最终结果:

$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   ├── file03.bin
│   ├── file0A.bin
│   ├── file0B.bin
│   └── file0C.bin
└── target
    ├── file01.bin
    ├── file02.bin
    ├── file03.bin
    ├── file0A.bin
    ├── file0B.bin
    └── file0C.bin
2 directories, 12 files

让我们回顾一下我们使用的选项:

  • -P:显示整个传输而不是单个文件的统计信息
  • -c:根据校验和跳过对象,而不是修改的时间和大小
  • -a:启用归档模式,等于:
    • -r:递归到目录
    • -l:将符号链接复制为符号链接
    • -p : 保留权限
    • -t:保留修改时间
    • -g:保留组
    • -o:保留所有者
    • -D : 保留设备文件和特殊文件
  • -u:跳过目标上较新的文件
  • -v:显示正在做什么的详细信息

我们还可以使用-n*选项进行试运行,这将在不进行任何更改的情况下模拟同步,以防止数据丢失*:

$ rsync -Pcauvn source/ target/
sending incremental file list
./
file01.bin
file02.bin
file03.bin
sent 189 bytes  received 28 bytes  434.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

3. 使用 Unison 程序

3.1.安装

Unison 源代码或二进制下载信息可在其主页上找到,在撰写本文时它指向此GitHub 存储库 。 我们使用64 位 Unison 2.51.4 和 OCaml 4.05.0 binary ,这给了我们这个包:

$ tree
.
├── bin
│   ├── unison
│   ├── unison-fsmonitor
│   └── unison-gtk2
├── LICENSE
└── README.md
1 directory, 5 files

3.2. 用法

我们可以通过运行unison-gtk可执行文件来使用 Unison 的 GUI 版本。在本文中,我们将使用基于文本的 Unison,它是unison可执行文件。

让我们查看我们的source目录和target目录的目录结构:

$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   └── file03.bin
└── target
    ├── file0A.bin
    ├── file0B.bin
    └── file0C.bin
2 directories, 6 files

当我们第一次进行同步时,我们会得到这个交互式提示:

$ ./unison ~/source/ ~/target/
Unison 2.51.4 (ocaml 4.05.0): Contacting server...
Looking for changes
Warning: No archive files were found for these roots, whose canonical names are:
	~/source
	~/target
This can happen either because this is the first time you have synchronized these roots, or because you have upgraded Unison to a new version with a different archive format.  
...
Donations to the Unison project are gratefully accepted: http://www.cis.upenn.edu/~bcpierce/unison
Press return to continue.[<spc>] 

我们可以按Enter继续,或按q退出。如果我们按下Enter,它会要求我们确认我们想要做的每一个改变。

我们按fEnter确认每个更改。我们可以按*><强制更改从左到右或从右到左传播,或者按/*跳过文件并保留两个对象。

当我们到达列表的末尾时,Unison 会再次询问我们是否要继续进行更改,我们可以按y继续,或者按q终止。

每次 Unison 要求输入时,我们可以按*?* 列出所有可能的响应及其含义:

Reconciling changes
source         target
file     ---->            file01.bin  [f] f
file     ---->            file02.bin  [f] f
file     ---->            file03.bin  [f] f
         <---- file       file0A.bin  [f] f
         <---- file       file0B.bin  [f] f
         <---- file       file0C.bin  [f] f
Proceed with propagating updates? [] y
Propagating updates
Unison 2.51.4 (ocaml 4.05.0) started propagating changes at 15:29:09.85 on 06 Jan 2022
[BGN] Copying file01.bin from ~/source to ~/target
[END] Copying file01.bin
[BGN] Copying file02.bin from ~/source to ~/target
[END] Copying file02.bin
[BGN] Copying file03.bin from ~/source to ~/target
[END] Copying file03.bin
[BGN] Copying file0A.bin from ~/target to ~/source
[END] Copying file0A.bin
[BGN] Copying file0B.bin from ~/target to ~/source
[END] Copying file0B.bin
[BGN] Copying file0C.bin from ~/target to ~/source
[END] Copying file0C.bin
Unison 2.51.4 (ocaml 4.05.0) finished propagating changes at 15:29:09.86 on 06 Jan 2022, 0.012 s
Saving synchronizer state
Synchronization complete at 15:29:09  (6 items transferred, 0 skipped, 0 failed)

完成后,如果我们再次尝试同步,它将什么也不同步:

$ ./unison ~/source/ ~/target/
Unison 2.51.4 (ocaml 4.05.0): Contacting server...
Looking for changes
Reconciling changes
Nothing to do: replicas have not changed since last sync.

如果我们删除或修改了一些文件并再次尝试同步,我们需要再次进行确认:

$ rm source/file0B.bin
$ echo "edit file" > source/file0C.bin
$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   ├── file03.bin
│   ├── file0A.bin
│   └── file0C.bin
└── target
    ├── file01.bin
    ├── file02.bin
    ├── file03.bin
    ├── file0A.bin
    ├── file0B.bin
    └── file0C.bin
2 directories, 11 files
$ ./unison ~/source/ ~/target/
Unison 2.51.4 (ocaml 4.05.0): Contacting server...
Looking for changes
Reconciling changes
source         target
deleted  ---->            file0B.bin  [f]
changed  ---->            file0C.bin  [f]
Proceed with propagating updates? [] y
Propagating updates
Unison 2.51.4 (ocaml 4.05.0) started propagating changes at 15:50:16.93 on 06 Jan 2022
[BGN] Updating file file0C.bin from ~/source to ~/target
[END] Updating file file0C.bin
[BGN] Deleting file0B.bin from ~/target
[END] Deleting file0B.bin
Unison 2.51.4 (ocaml 4.05.0) finished propagating changes at 15:50:16.93 on 06 Jan 2022, 0.003 s
Saving synchronizer state

如果我们想在没有任何交互式提示的情况下立即与默认的 Unison 设置同步,其中将传播非冲突的更改并跳过冲突,我们可以将-batch=true*作为选项*传递:

$ ./unison -batch=true ~/source/ ~/target/
Unison 2.51.4 (ocaml 4.05.0): Contacting server...
Looking for changes
Reconciling changes
changed  ---->            file0C.bin  
source       : changed file       modified on 2022-01-06 at 16:40:13  size 11        rw-r--r--
target       : unchanged file     modified on 2022-01-06 at 16:28:13  size 11        rw-r--r--
Propagating updates
Unison 2.51.4 (ocaml 4.05.0) started propagating changes at 16:40:35.93 on 06 Jan 2022
[BGN] Updating file file0C.bin from ~/source to ~/target
[END] Updating file file0C.bin
Unison 2.51.4 (ocaml 4.05.0) finished propagating changes at 16:40:35.93 on 06 Jan 2022, 0.000 s
Saving synchronizer state
Synchronization complete at 16:40:35  (1 item transferred, 0 skipped, 0 failed)

4. 使用 Zaloha2 Bash 脚本

4.1.安装

Zaloha2 是一个 Bash 脚本形式的小而简单的目录同步器。我们可以从它的GitHub 存储库 下载它。 下载项目后,我们将获得以下包:

$ tree
.
├── DOCUMENTATION.md
├── LICENSE
├── README.md
├── Simple_Demo_screenshot.png
├── Simple_Demo_step1.sh
├── Simple_Demo_step2.sh
├── Simple_Demo_step3.sh
├── Simple_Demo_step4.sh
├── Simple_Demo_step5.sh
├── Simple_Demo_step6.sh
├── Simple_Demo_step7.sh
└── Zaloha2.sh
0 directories, 12 files

我们需要使Zaloha2.sh可执行,然后我们就可以使用它了:

$ sudo chmod u+x Zaloha2.sh

4.2. 用法

使用 Zaloha2 非常简单。首先,假设我们有以下目录结构:

$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   └── file03.bin
└── target
    ├── file0A.bin
    ├── file0B.bin
    └── file0C.bin
2 directories, 6 files

然后我们运行同步过程:

$ ./Zaloha2.sh --sourceDir="/home/blogdemo/source" --backupDir="/home/blogdemo/target" --color --revUp --revNew --noRemove --sha256 --findSourceOps='( -type d -a -name subdir ) -prune -o' --findSourceOps='( -type d -a -name .svn ) -prune -o' --findSourceOps='( -type d -a -name .cache ) -prune -o' --findGeneralOps=
TO BE COPIED TO /home/blogdemo/target/
===========================================
NEW       file01.bin
NEW       file02.bin
NEW       file03.bin
MKDIR     subdir2
NEW       subdir2/file05.bin
Execute above listed copies to /home/blogdemo/target/ ?
[Y/y=Yes, S/s=do nothing and show further, other=do nothing and abort]: y
    mkdir /home/blogdemo/target/subdir2
    cp --preserve=timestamps /home/blogdemo/source/file01.bin /home/blogdemo/target/file01.bin
    cp --preserve=timestamps /home/blogdemo/source/file02.bin /home/blogdemo/target/file02.bin
    cp --preserve=timestamps /home/blogdemo/source/file03.bin /home/blogdemo/target/file03.bin
    cp --preserve=timestamps /home/blogdemo/source/subdir2/file05.bin /home/blogdemo/target/subdir2/file05.bin
TO BE REVERSE-COPIED TO /home/blogdemo/source/
===========================================
FROM COMPARING CONTENTS OF FILES: TO BE COPIED TO /home/blogdemo/target/
===========================================
$
$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   ├── file03.bin
│   ├── subdir
│   │   └── file04.bin
│   └── subdir2
│       └── file05.bin
└── target
    ├── file01.bin
    ├── file02.bin
    ├── file03.bin
    ├── file0A.bin
    ├── file0B.bin
    ├── file0C.bin
    └── subdir2
        └── file05.bin
5 directories, 12 files

让我们检查一下我们使用的选项:

  • –sourceDir : 源目录
  • –backupDir : 目标目录
  • –color:在交互式提示期间为更改着色
  • –revUp:反向更新;如果目标中有较新的对象,请将它们复制到源
  • –revNew:反向新;如果目标中存在源中不存在的对象并且它们比上次运行的 Zaloha2 更新,则将它们复制到源中
  • –noRemove:不移除目标中的对象;仅添加或更新目标中的现有对象
  • –sha256 : 除了比较文件大小和修改日期,检查文件 SHA256 校验和
  • –noProgress:不显示来自分析阶段的消息
  • –findSourceOps:从同步中排除某些子目录。如果目标中存在这样的子目录,则将其删除
  • –findGeneralOps:如果它为空,它将禁用排除某些目录的内部定义的默认值(“trash”或“lost+found”)。也可以用来代替findSourceOps来一般排除文件或目录

rsync或 Unison 不同,Zaloha2 最初不会将文件或目录从目标反向复制到源,除非这些文件或目录比上次运行的 Zaloha2 更新

所以,让我们更新目标目录中的一些文件,然后再次运行 Zaloha2 以触发反向复制:

$ echo "edit" > target/file0B.bin 
$ echo "edit" > target/file0C.bin 
$ echo "edit" > target/subdirB/file0E.bin 
$
$ ./Zaloha2.sh --sourceDir="/home/blogdemo/source" --backupDir="/home/blogdemo/target" --color --revUp --revNew --noRemove --sha256 --findSourceOps='( -type d -a -name subdir1 ) -prune -o' --findSourceOps='( -type d -a -name subdirA ) -prune -o' --findGeneralOps=
TO BE COPIED TO /home/blogdemo/target/
===========================================
TO BE REVERSE-COPIED TO /home/blogdemo/source/
===========================================
REV.NEW   file0A.bin
REV.NEW   file0B.bin
REV.NEW   file0C.bin
REV.MKDI  subdirB
REV.NEW   subdirB/file0E.bin
Execute above listed reverse-copies to /home/blogdemo/source/ ?
[Y/y=Yes, S/s=do nothing and show further, other=do nothing and abort]: y
    '[' '!' -e /home/blogdemo/source/file0A.bin ']'
    '[' '!' -e /home/blogdemo/source/file0B.bin ']'
    '[' '!' -e /home/blogdemo/source/file0C.bin ']'
    '[' '!' -e /home/blogdemo/source/subdirB ']'
    mkdir /home/blogdemo/source/subdirB
    '[' '!' -e /home/blogdemo/source/subdirB/file0E.bin ']'
    cp --preserve=timestamps /home/blogdemo/target/file0A.bin /home/blogdemo/source/file0A.bin
    cp --preserve=timestamps /home/blogdemo/target/file0B.bin /home/blogdemo/source/file0B.bin
    cp --preserve=timestamps /home/blogdemo/target/file0C.bin /home/blogdemo/source/file0C.bin
    cp --preserve=timestamps /home/blogdemo/target/subdirB/file0E.bin /home/blogdemo/source/subdirB/file0E.bin
FROM COMPARING CONTENTS OF FILES: TO BE COPIED TO /home/blogdemo/target/
===========================================
$
$ tree
.
├── source
│   ├── file01.bin
│   ├── file02.bin
│   ├── file03.bin
│   ├── file0A.bin
│   ├── file0B.bin
│   ├── file0C.bin
│   ├── subdir1
│   │   └── file04.bin
│   ├── subdir2
│   │   └── file05.bin
│   └── subdirB
│       └── file0E.bin
└── target
    ├── file01.bin
    ├── file02.bin
    ├── file03.bin
    ├── file0A.bin
    ├── file0B.bin
    ├── file0C.bin
    ├── subdir2
    │   └── file05.bin
    ├── subdirA
    │   └── file0D.bin
    └── subdirB
        └── file0E.bin
8 directories, 18 files

如果我们想设置自动/非交互模式,有一些步骤有点复杂,虽然很灵活,但作者在文档中建议了这些步骤。

我们需要做的是运行带有额外选项的命令*–noExec*,它只会生成脚本(脚本存储在目标目录中的*.Zalora_metadata*中)。然后,我们需要编写一个包装脚本来执行这些脚本。请参阅自动操作部分中的Zaloha2 文档

Zaloha2 项目仍在积极开发中——谨慎使用

5. 使用 FreeFileSync 程序

5.1.安装

FreeFileSync 是开源的多平台软件。它是基于 GUI 的,但也提供了执行批处理/非交互模式的功能。

我们可以从其主页下载 FreeFileSync 安装程序脚本并运行它,这将创建桌面快捷方式。

5.2. 用法

这是 FreeFileSync 的主要 UI:

/uploads/synchronize_linux_directories/2.jpg
自由文件同步

界面非常直观:

  1. 更改比较设置,例如根据文件修改时间和大小、文件内容或文件大小进行比较。
  2. 开始比较两个目录。
  3. 过滤器:我们可以根据文件名、扩展名、大小或修改时间来包含或排除某些文件或目录。
  4. 更改同步设置:我们可以同步两个方向(Two way),单向(Mirror),只更新(Update),或者自定义。
  5. 开始同步。
  6. 源目录。
  7. 目标目录。

如果我们要创建批处理/非交互模式,首先我们需要选择源目录和目标目录,然后单击File > Save as batch job…,将显示以下对话框:

/uploads/synchronize_linux_directories/1.jpg

我们需要禁用进度对话框选项并启用忽略错误选项。否则,它将显示交互/错误消息框并无限期地等待交互。 创建批处理作业文件后,例如BatchRun.ffs_batch,我们需要设置 FreeFileSync 来运行它。

例如,我们可以将cron作业设置为每天凌晨 1 点运行它:

$ crontab -e
0 1 * * * <FreeFileSync installation path>/FreeFileSync <batch file path>/BatchRun.ffs_batch

6.比较

6.1.特征

工具 编程语言 执照 双向 增量复制 检测冲突 传播删除 修订控制 调度或服务
rsync C GPL v3 是的 是的 是的 是的,使用*–delete* 是的,使用*–backup和带时间戳的–suffix* 是,通过操作系统
Unison 主要是OCaml GPL 是的 是的 是的 是的 是的 是,通过操作系统
Zaloha2 重击 是的 是的 是的 是的 是,通过操作系统
FreeFileSync C++ GPL 是的 是的 是的 是的 是的 是,通过操作系统

6.2. 基准

我们测量了以下系统和数据的性能:

  • Dell Latitude Intel Core i7-6600U CPU @ 2.60GHz × 4, 16GB RAM
  • 源码目录:希捷外置硬盘5TB、7200rpm、SATA III、ext4
  • 目标目录:希捷外置硬盘 1.5TB、7200rpm、SATA III、NTFS
  • 数据同步:10GB,11,780 个文件
  • 操作系统:Debian 克星

这是源目录的文件数和总大小:

$ find source -type f | wc -l
11780
$
$ du source -hc --max-depth=0
1.3G source/subdir1
3.3G source/subdir2
5.5G source/subdir3
10G source
10G total

我们通过运行命令 3 次来测试每种方法rsync除外——6次——因为它不是双向的):

  • 对于第一次运行,我们将数据从source目录同步/镜像到target目录。
  • 对于第二次运行,没有什么可做的,所以所有工具都立即返回。
  • 然后,在第三次运行中,首先我们删除了 source 中的一些数据(删除 subdir1),并在 target 中添加了一些数据(将 subdir1 重命名为 subdir4),然后同步了目录。

我们用于每个命令的选项:

  • rsync
$ rsync -PScauv --info=progress2 source/ /media/blogdemo/target/
$ rsync -PScauv --info=progress2 target/ /media/blogdemo/source/
  • Unison
$ ./unison -batch=true /media/blogdemo/source/ /media/blogdemo/target/
  • Zaloha2
$ ./Zaloha2.sh --sourceDir="/media/blogdemo/source" --backupDir="/media/blogdemo/target" --color --revUp --revNew --noRemove --noRestore --mawk --findGeneralOps=
  • FreeFileSync
$ which FreeFileSync
/home/blogdemo/.local/bin/FreeFileSync
$ ./home/blogdemo/.local/bin/FreeFileSync BatchRun.ffs_batch

结果:

工具 第一次运行 第二次运行 第三次运行
rsync 15:44 00:01 15:48
Unison 07:31 00:01 00:04
Zaloha2 05:22 00:01 失败(无反向更新)
FreeFileSync 05:10 00:01 00:13