自己写个 `rsync` + `fswatch` 实时增量同步脚本,干掉 Cursor AI、Sublime Text 的SFTP等 插件!

发布于:2025-07-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

在这里插入图片描述

自己写个 rsync + fswatch 实时增量同步脚本,干掉 Cursor AI、Sublime Text 的 SFTP等 插件!

作为一个码农,我最头疼的事情之一就是编辑器同步代码到服务器这块。用过各种各样的sftp、rsync插件,感觉不好用。。

我琢磨着:难道就没有既快速,又安全,还能只上传修改过文件差异的解决方案?最终,我自己动手写了个基于 rsync + fswatch 的实时增量同步脚本,实现了只同步新增和修改文件,不做全量同步,支持任何编辑器,简单又靠谱。


说说这几个插件的坑

  • Cursor AI 的 SFTP:传输速度慢得哭,尤其项目文件多一点,等得我花儿都谢了。
  • Sublime Text 的 SFTP:要收费,虽说速度能快点,但不支持 rsync,上传的是整个文件,没法只传差异处,浪费流量和时间。

我的解决思路

为什么不自己写个脚本,用 rsync 来同步,只针对新增和修改的文件?而且还不被编辑器限制?

  • 只上传改动的文件,大大节省时间和带宽
  • fswatch 实时监听文件变化,做到自动触发同步
  • 支持配置排除目录,避免上传 .gitnode_modules 等垃圾文件夹
  • 彩色输出日志,清晰明了,方便调试
  • 同步成功还能播放提示音,避免盯屏幕发呆
  • 任何编辑器都能用,不依赖插件,灵活又安全

环境准备

  • macOS/Linux,本文以 macOS 为例
  • 安装 fswatch
  brew install fswatch
  • 安装 jq
  brew install jq
  • 配置好 SSH 免密登录服务器

配置文件示例(rsync_config.json

{
  "remote_host": "11.22.33.44",
  "remote_user": "root",
  "remote_path": "/www/wwwroot/test.myprojec.com",
  "exclude": [".git", "node_modules", "dist"]
}

核心脚本:rsync_project.sh

#!/bin/bash

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
RESET='\033[0m'

# 获取脚本路径
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TIMESTAMP=$(date +%s)
LOG_FILE="$SCRIPT_DIR/.rsync_sync_${TIMESTAMP}.log"

if [[ -z "$1" ]]; then
  echo -e "${RED}[错误] 请输入项目根目录路径,比如:sh rsync_project.sh ~/my-project${RESET}"
  exit 1
fi

PROJECT_DIR="$(cd "$1" && pwd)"
CONFIG_FILE="$PROJECT_DIR/rsync_config.json"
DETAILED=false

for arg in "$@"; do
  if [[ "$arg" == "--detail" ]]; then
    DETAILED=true
  fi
done

if [[ ! -f "$CONFIG_FILE" ]]; then
  echo -e "${RED}[错误] 找不到配置文件:$CONFIG_FILE${RESET}"
  exit 1
fi

if ! command -v jq &> /dev/null; then
  echo -e "${RED}[错误] 你需要先装 jq:brew install jq${RESET}"
  exit 1
fi

if ! command -v fswatch &> /dev/null; then
  echo -e "${RED}[错误] 你需要先装 fswatch:brew install fswatch${RESET}"
  exit 1
fi

REMOTE_HOST=$(jq -r '.remote_host' "$CONFIG_FILE")
REMOTE_USER=$(jq -r '.remote_user' "$CONFIG_FILE")
REMOTE_PATH=$(jq -r '.remote_path' "$CONFIG_FILE")

EXCLUDE_ARR=()
for line in $(jq -r '.exclude[]?' "$CONFIG_FILE"); do
  EXCLUDE_ARR+=("${line%/}")
done

if [[ -z "$REMOTE_HOST" || -z "$REMOTE_USER" || -z "$REMOTE_PATH" ]]; then
  echo -e "${RED}[错误] 配置文件缺少 remote_host、remote_user 或 remote_path${RESET}"
  exit 1
fi

echo -e "${BLUE}[测试] 尝试连接 $REMOTE_USER@$REMOTE_HOST ...${RESET}"
ssh -q -o ConnectTimeout=5 "$REMOTE_USER@$REMOTE_HOST" "echo pong" 2>/dev/null
if [[ $? -ne 0 ]]; then
  echo -e "${RED}[失败] 无法连接服务器 $REMOTE_USER@$REMOTE_HOST,请检查 SSH 配置${RESET}"
  exit 1
fi

echo -e "${GREEN}[] 成功连接服务器:$REMOTE_USER@$REMOTE_HOST${RESET}"
echo -e "${CYAN}目标路径:$REMOTE_PATH${RESET}"
$DETAILED && echo -e "${YELLOW}[模式] 详细日志已启用${RESET}"
echo -e "${BLUE}[日志] 同步记录保存到:$LOG_FILE${RESET}"

HAS_OSASCRIPT=false
command -v osascript &> /dev/null && HAS_OSASCRIPT=true

echo -e "${BLUE}[监听中] 等待文件变更...${RESET}"

fswatch -0 -e "\\.DS_Store$" "$PROJECT_DIR" | while IFS= read -r -d "" changed_path; do
  skip=false
  for excl in "${EXCLUDE_ARR[@]}"; do
    [[ "$changed_path" == *"/$excl/"* || "$changed_path" == *"/$excl"* ]] && skip=true && break
  done
  $skip && continue
  [[ ! -f "$changed_path" ]] && continue

  REL_PATH="${changed_path#"$PROJECT_DIR"/}"
  CURRENT_TIME=$(date '+%Y-%m-%d %H:%M:%S')

  echo ""
  echo -e "${BLUE}[变更] 文件新增或修改:$REL_PATH${RESET}"
  echo -e "${MAGENTA}[时间] $CURRENT_TIME${RESET}"

  RSYNC_CMD=(rsync -az -e "ssh")
  RSYNC_CMD+=(--rsync-path="mkdir -p \"$(dirname "$REMOTE_PATH/$REL_PATH")\" && rsync")
  RSYNC_CMD+=("$changed_path" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/$REL_PATH")

  if $DETAILED; then
    echo -e "${CYAN}[执行] rsync 命令:${RESET}"
    printf '%q ' "${RSYNC_CMD[@]}"
    echo ""
  else
    echo -e "${CYAN}[执行] 正在同步文件...${RESET}"
  fi

  echo "[$CURRENT_TIME] 变更文件: $REL_PATH" >> "$LOG_FILE"
  "${RSYNC_CMD[@]}" >> "$LOG_FILE" 2>&1

  if [[ $? -eq 0 ]]; then
    # 同步成功后设置 chown
    ssh "$REMOTE_USER@$REMOTE_HOST" "chown www:www \"$REMOTE_PATH/$REL_PATH\"" >> "$LOG_FILE" 2>&1

    echo -e "${GREEN}[完成] 同步成功 ✅ 并设置权限为 www:www${RESET}"
    echo "[$CURRENT_TIME] ✅ 同步成功并设为 www" >> "$LOG_FILE"

    if command -v afplay &> /dev/null; then
      afplay /System/Library/Sounds/Tink.aiff &
    fi
    $HAS_OSASCRIPT && osascript -e 'display notification "文件同步成功" with title "rsync" sound name "Glass"'
  else
    echo -e "${RED}[失败] 同步失败 ❌${RESET}"
    echo "[$CURRENT_TIME] ❌ 同步失败" >> "$LOG_FILE"
    $HAS_OSASCRIPT && osascript -e 'display notification "文件同步失败" with title "rsync" sound name "Basso"'
  fi
done

使用方法

bash rsync_project.sh myprojec --detail

替换 myprojec 为你的项目目录,--detail 参数可选,开启详细日志。


结语

用这套脚本后,文件改动几乎立刻上传,速度杠杠的,且不乱上传无用文件,安全又高效。再也不用担心编辑器 SFTP 插件各种坑,支持所有编辑器,只要能保存文件,统统适用。

如果你也被那些插件折磨过,不妨试试我这套 rsync + fswatch 脚本,真心好用!

欢迎大家留言交流,遇到问题咱们一起吐槽、优化!

Happy coding! 🚀


网站公告

今日签到

点亮在社区的每一天
去签到