之前写过一篇文章 RemotelySave+InfiniCloud最简单的多端同步方案实践 ,这篇文章中我使用了 Unison 做文件的双向同步,这里我详细介绍一下这个软件的使用。
为什么选择它
开源,免费,多平台支持,轻量级,占用资源小,同步速度快,稳定,得到市场的验证。最关键的是它基于事件同步,即当你的文件夹发生改变时才同步,当然它也可以定时同步,支持增量同步和全量同步,支持本地和远程同步等。
它多小,Unix-like 系统一般 1M 多到 3M 多左右,Windows 30M 左右,因为它要打包 unix-like 的资源文件。占用资源也很小,监听期间 cpu 占用率为 0,同步期间也就百分之零点几。它多快,本地文件夹几乎是秒级的,还没发现它动,它已经动了,我 100 多个文件,复制完即同步完了。至于远程同步,这取决于你的网速和对方网速了。我的网速一般般,用 InfiniCloud 做远程同步一般 2-3 秒左右,日常使用基本无感,接近实时。
安装
安装特简单,完全绿色,下载完成即安装完成,如果你要全局可用,Windows 需要把程序路径添加到环境变量,Mac 直接 brew install unison
完事。
下载地址:Release v2.53.4 · bcpierce00/unison · GitHub
另外,如果 Mac 想支持文件夹变化时同步,还需要安装下 fsevents 支持,建议直接安装 GitHub - autozimu/unison-fsmonitor: unison-fsmonitor implementation 完事。
使用
使用也很简单,只需要按帮助文档设置好参数即可。
Windows
unison "D:\demo1" "D:\demo2" -perms 0o1622 -fastcheck true -batch -auto -silent -repeat watch+3600 -retry 3 -ignore "Name .git" -ignore "Name ." -ignore "Name .cache" -ignore "Name .trash"
Mac
unison "~/demo1" "~/demo2" -perms 0o1622 -fastcheck true -batch -auto -silent -repeat watch+3600 -retry 3 -ignore "Name .git" -ignore "Name ." -ignore "Name .cache" -ignore "Name .trash"
这里的 demo1 和 demo2 是双向同步的两个文件夹,需要改成你自己的目录。
这个命令行主要是配置 Unison 进行快速、静默、自动化的增量同步,并忽略某些文件或目录,同时设置文件权限。同时,它将持续监控文件系统变化并在检测到变化后立即自动同步,并且每一小时会做一次全量扫描,每次同步失败时最多重试 3 次。
参数说明
-
-perms 0o1622
:
用于指定在同步过程中如何处理文件权限(mode bits)。这里的数字 0o1622 是一个八进制数字,它代表了 Unix/Linux 系统中的文件权限掩码。0o 代表八进制,622 代表权限,当你的目录权限不足时可以调整这个数值。当你添加 -perms 参数时,Unison 将在同步文件和目录内容的同时,也会同步文件和目录的权限属性。 -
-fastcheck true
:
这个选项告诉 Unison 在比较两个目录时先进行快速检查,利用上次同步时记录的元数据信息,仅比较自上次同步以来可能发生变化的文件。这样可以大大提高同步速度,尤其是在大型目录树中。 -
-batch
:
使用此选项,Unison 将以非交互模式运行,所有需要用户确认的操作都将自动执行,不会停下来询问用户。 -
-auto
:
同样表示以非交互模式运行,并且在发生冲突时自动解决冲突,选择最后一次修改的版本。 -
-silent
:
运行时静默模式,只显示错误消息,不显示同步进度和同步详情。 -
-repeat watch+3600
:
设置 Unison 持续监控并立即同步,这里的watch+3600
表示每 3600 秒(即 1 小时)扫描一次文件系统变更,在检测到变更时立即执行同步。这里 watch 和数字可以单独使用,仅数字,即定期多少秒同步一次,仅 watch 则仅监控文件变化时同步,二者结合则两者同时进行。 -
-retry 3
:
如果同步过程中发生错误,Unison 将尝试重试 3 次。这对于网络不稳定或者其他暂时性错误的情况很有帮助。 -
-ignore
:
这个选项后面通常跟着一个模式或者一组模式,用于指定需要忽略的文件或目录。例如:-ignore "Name .git"
将忽略名为.git
的文件或目录。
开机运行
Windows 建议把脚本放到启动目录或放到定时任务里,如果放到定时任务里注意防止脚本多次启动。
比如:
@echo off
set SCRIPT_NAME=%~nx0
set COUNT=
for /F "delims=" %%i in ('wmic process get Caption^,CommandLine ^| find /i "%SCRIPT_NAME%" ^| find /v /c "find.exe"') do (
set COUNT=%%i
)
if %COUNT% GTR 1 (
echo 监控进程已存在
exit /B 1
)
rem 脚本主体内容...
Mac 建议放到登录启动或者 crontab 里,crontab 里注意防止脚本多次启动。
比如:
SCRIPT_NAME=$(basename "$0")
if [ "$(pgrep -fl "$SCRIPT_NAME")" ]; then
echo "监控进程已存在"
exit 0
fi
# 脚本主体内容...
注意,这种判断方式并不严格,严格方式建议使用文件锁,为防止死锁还应启动死锁清理进程。
这里还有一个要注意的点,通常我们会开机挂载webdav,你的监控脚本不能早于webdav成功挂载前执行,否则会监控失败,建议监控前先判断下目录是否存在,比如下面的shell代码:
webdav_dir="/data/your webdav dir"
mynotes_dir="/data/your notes dir"
# 循环检查目录是否存在
while true; do
# 判断 $webdav_dir 是否存在
if [ -d "$webdav_dir" ]; then
# 判断 $mynotes_dir 是否存在
if [ -d "$mynotes_dir" ]; then
# 如果两个目录都存在,则退出循环并执行后续命令
echo "will start unison"
break
fi
fi
# 如果任意一个目录不存在,则等待一段时间后再次检查
echo "waiting for $webdav_dir and $mynotes_dir"
sleep 5
done
# 在这里放置你启动Unison的代码
或 Windows bat代码:
@echo off
set "webdav_dir=d:\your webdav dir"
set "mynotes_dir=d:\your notes dir"
:check_dirs
IF EXIST "%webdav_dir%" (
IF EXIST "%mynotes_dir%" (
echo will start unison
goto :start_unison
)
)
echo waiting for "%webdav_dir%" and "%mynotes_dir%"
timeout /t 5 /nobreak > nul
goto :check_dirs
:start_unison
rem 在这里放置你启动Unison的代码
下载演示文件:demo.zip
深入了解
镜像同步
Unison 不仅可以进行双向同步,也可以进行单向同步。在 Unison 中,可以通过指定 -force
和 -prefer
选项来实现单向同步。
例如,如果您想从目录 A 单向同步到目录 B,可以使用如下命令:
unison -force srcdir trgdir -prefer srcdir
在这里:
-force
表示强制执行同步,即使目标目录中有些文件比源目录中的新或不同,也覆盖目标目录的内容。-prefer srcdir
表示在有冲突的情况下,优先使用 srcdir(源目录)中的文件。
不过要注意的是,单纯这样使用可能不会阻止 Unison 尝试对比并同步两个方向的所有文件。如果只需要单向复制(而非同步,即只复制源目录到目标目录,而不关心目标目录中新增或修改的内容),可以结合使用 -ignorenew
选项,忽略目标目录中新增的文件:
unison -force srcdir trgdir -prefer srcdir -ignorenew trgdir
这会告诉 Unison 忽略目标目录中所有新增的文件和目录,只将源目录的内容复制到目标目录。
当然,具体情况还需要根据实际需求来调整 Unison 的参数。
增量和全量同步
在 Unison 文件同步工具中,同步既可以是增量的也可以是全量的,具体取决于你的同步策略和参数设置。Unison 本身的设计是基于增量同步,即它会比较两个目录之间的差异,并仅同步那些发生变化的部分。但你也可以通过适当的设置来模拟全量同步的效果。
增量同步(默认行为)
Unison 默认执行的就是增量同步。只要之前同步过,Unison 会记住以前的状态,并只同步自上次同步以来更改过的文件。执行同步的命令很简单,例如:
unison source_directory target_directory
全量同步(模拟)
如果你想进行一次全量同步,无视过去的同步历史,可以使用 -ignorearchives
选项,这样 Unison 会重新计算所有文件的哈希值,当作首次同步一样处理:
unison -ignorearchives source_directory target_directory
请注意,全量同步会耗费更多的时间和资源,因为它会重新比较所有文件,而不是仅比较自上次同步以来改动的文件。在某些情况下,为了确保一致性,你可能需要清除 Unison 的.pstate 隐藏文件夹(包含同步历史的文件),然后进行全量同步。
在实际使用中,Unison 还提供了许多选项可以帮助你精细化控制同步行为,比如排除特定文件、按文件属性进行同步等,具体可查阅 Unison 的官方文档以了解更多信息。