Unison实现文件双向同步的利器

之前写过一篇文章 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 次。

参数说明

  1. -perms 0o1622
    用于指定在同步过程中如何处理文件权限(mode bits)。这里的数字 0o1622 是一个八进制数字,它代表了 Unix/Linux 系统中的文件权限掩码。0o 代表八进制,622 代表权限,当你的目录权限不足时可以调整这个数值。当你添加 -perms 参数时,Unison 将在同步文件和目录内容的同时,也会同步文件和目录的权限属性。

  2. -fastcheck true
    这个选项告诉 Unison 在比较两个目录时先进行快速检查,利用上次同步时记录的元数据信息,仅比较自上次同步以来可能发生变化的文件。这样可以大大提高同步速度,尤其是在大型目录树中。

  3. -batch
    使用此选项,Unison 将以非交互模式运行,所有需要用户确认的操作都将自动执行,不会停下来询问用户。

  4. -auto
    同样表示以非交互模式运行,并且在发生冲突时自动解决冲突,选择最后一次修改的版本。

  5. -silent
    运行时静默模式,只显示错误消息,不显示同步进度和同步详情。

  6. -repeat watch+3600
    设置 Unison 持续监控并立即同步,这里的 watch+3600 表示每 3600 秒(即 1 小时)扫描一次文件系统变更,在检测到变更时立即执行同步。这里 watch 和数字可以单独使用,仅数字,即定期多少秒同步一次,仅 watch 则仅监控文件变化时同步,二者结合则两者同时进行。

  7. -retry 3
    如果同步过程中发生错误,Unison 将尝试重试 3 次。这对于网络不稳定或者其他暂时性错误的情况很有帮助。

  8. -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 的官方文档以了解更多信息。

官方文档

Home · bcpierce00/unison Wiki · GitHub

2 个赞

666,这是我想要的

windows自带的robocopy也行

1 个赞

想知道这个和freefilesync相比,优点是什么

我放弃freefilesync的原因:

  1. 同步时会产生.ffs_lock和.ffs_tmp的临时文件,这种侵入式设计会产生副作用,你可以看我之前的一篇文章RemotelySave+InfiniCloud最简单的多端同步方案实践中的架构图,因为我用了iCloud,这种临时文件会引发iCloud频繁同步,不仅浪费宽带还占系统资源。

  2. freefilesync的同步不是基于事件,是基于定时同步,这种方式实际上很耗费资源。

  3. 耗费资源严重,我在运行freefilesync期间,开启5秒钟自动同步,它同步扫描也将近耗费5秒,这会导致一直在同步,如果设置间隔太大可能又同步不及时。

总之,就是同步慢,耗资源,有副作用。也许我使用不当造成的,有高手请指点,但目前没有找到解决办法。

unison 有比较简洁一点的使用介绍吗,看官方的 wiki 可以用一本来形容了 :disappointed_relieved: ,另外如何实现本地<->webdav 或者 oss 间的双向同步

简洁是相对概念,我觉得这篇文章已经很简洁了,只介绍了基本使用。如果你觉得哪里不好理解可以搜索下具体问题或者问问Ai。还可以看看官方帮助文档。这是我用翻译软件翻译的命令参数:https://wilson.lovestoblog.com/mynote/工具软件/unison帮助文档翻译.html

本地<->webdav/oss 并不能直接支持,不过你可以把webdav/oss映射为本地磁盘,然后再同步。如果有ssh,Unison是支持的。

unison同步时遇到文件冲突是如何处理的,是选择保留最新的还是文件大的,能不能采用重命名的方式,两个文件都保留