fswatch 的使用

fswatch -r -x --event Created --event Updated --event Removed --event Renamed --event MovedTo --exclude="\.DS_Store" --exclude="\.git" "$src_dir"

这条命令是监控 $src_dir 文件夹下的创建,修改,删除,重命名,移动等事件,排除.DS_Store 和.git 文件,每个事件都会触发一次。

-r 递归监控子目录目录下的所有文件
-o 这个参数会使 fswatch 输出每次变化发生时的文件数,而不是文件列表

比如:fswatch -or -x --event Created --event Updated --event Removed --event Renamed --event MovedTo "$src_dir" 监控 $src_dir 文件夹下的创建,修改,删除,重命名,移动等事件,返回的是发生变化的文件数。

可以使用 read 命令读取事件发生的文件列表,比如:

fswatch -x --event Created "$src_dir" | while read -r line; do
echo $line
done

read -r 选项表示进行原始读取(raw read),即在读取输入时不进行反斜杠 \n \t 的转义处理

rsync 的使用

rsync -rtuv --delete "$SOURCE_DIR/" "$DEST_DIR/"

这里,-r表示递归同步,-t保持文件时间戳,-u表示只同步更新过的文件,-v表示详细输出,--delete会在目标目录删除源目录不存在的文件。

也可以使用-a 选项,比如:

rsync -avz --delete /path/to/source_directory/ /path/to/destination_dir
  • -a 是归档模式,它保留了递归、符号链接处理、权限保持,用户/组,时间等多项功能,并且能递归同步目录。
  • -v 是详细模式,会让rsync在执行时输出更多信息,便于观察同步过程。
  • -z 表示在传输时进行压缩,加快传输速度,特别是对于大文件或慢速网络连接。
  • --delete 选项确保目标目录中如果存在源目录没有的文件,则将其删除,保持两个目录内容一致。

实践

下面,让 fswatch 和 rsync 相结合实现,当文件变化时,单向备份文件的功能。

#!/usr/bin/env bash

# * * * * * /bin/bash /pah/bin/mynotes_backup.sh >/dev/null 2>&1 &
# nohup bash /path/bin/mynotes_backup.sh >/dev/null 2>&1 &

# 监控的根目录和批处理文件
src_dir="/path/Documents/我的笔记"
backup_idr="/path/Documents/mynote_backup"
fswatch="/usr/local/bin/fswatch"

# fswatch命令行参数,监控所有类型的事件
EVENTS='--event Created --event Updated --event Removed --event Renamed --event MovedTo'
EXCLUDES=(--exclude="\.DS_Store" --exclude="media/\.git" --exclude="\.git" --exclude="未命名\.md")

SCRIPT_NAME=$(basename "$0")
if [ "$(pgrep -fl "$SCRIPT_NAME")" ]; then
echo "$SCRIPT_NAME 进程已存在"
exit 0
fi

lock="/tmp/$SCRIPT_NAME.lock"

# 启动fswatch监听目录,监听多个目录,可以在webpage_dir后面中添加多个,用空格分开
#fswatch参数说明
# -o 这个参数会使 fswatch 输出每次变化发生时的文件数,而不是文件列表
# -r 递归监控子目录目录下的所有文件
# read -r 选项表示进行原始读取(raw read),即在读取输入时不进行反斜杠 \n \t 的转义处理
$fswatch -or "${EXCLUDES[@]}" -x $EVENTS "$src_dir" | while read -r num; do

#echo "change nums: $num   date: $(date +'%Y-%m-%d %H:%M:%S')"

if [ ! -f "$lock" ]; then

# 5秒钟内,不再接受文件变化事件
touch $lock
# 这里开启一个后台进程,5秒后删除锁文件
(sleep 5 && rm -f $lock) &

# echo "----------- find changes -----------"

# 调用同步脚本 &的作用是避免rsync命令阻塞循环,导致监听的事件不能马上处理
# 这样会在rsync命令执行完后,再次执行rsync命令,引起多次同步
# 如果不阻塞循环,则后续事件到来会立即启动rsync命令,rsync命令执也判断一次自身是否已启动
# 如果已启动则退出,确保每次监控变化,只执行一次
# & 保证rsync不会阻塞循环执行
# pgrep -x 精确匹配进程名
# rsync -a(归档模式)确保了文件的创建、更新、删除都会被同步
#       -v 显示是详细模式,会让rsync在执行时输出更多信息,便于观察同步过程
#       -z 表示在传输时进行压缩,加快传输速度,特别是对于大文件或慢速网络连接
#       --delete 选项确保目标目录中如果存在源目录没有的文件,则将其删除,保持两个目录内容一致(这里暂时不用删除)
if [ ! "$(pgrep -x rsync)" ]; then
# echo "----------- start rsync -----------"
rsync -avz "$src_dir" "$backup_idr" --exclude='.DS_Store' --exclude='.git' --exclude='media/.git' --exclude='未命名.md' &
# rsync -avz --delete "$src_dir" "$backup_idr" --exclude='.DS_Store' --exclude='.git' --exclude='media/.git' &
fi

fi

done

这里也可以采用定时同步方式,看使用场景,比如:

while true; do rsync -avz --delete "/path/to/source/" "/path/to/destination"; sleep 60; done

每60秒执行一次,保持同步。这种方法简单但效率不高,因为它不论文件是否变化都会尝试同步。