自己写个 rsync
+ fswatch
实时增量同步脚本,干掉 Cursor AI、Sublime Text 的 SFTP等 插件!
作为一个码农,我最头疼的事情之一就是编辑器同步代码到服务器这块。用过各种各样的sftp、rsync插件,感觉不好用。。
我琢磨着:难道就没有既快速,又安全,还能只上传修改过文件差异的解决方案?最终,我自己动手写了个基于 rsync
+ fswatch
的实时增量同步脚本,实现了只同步新增和修改文件,不做全量同步,支持任何编辑器,简单又靠谱。
说说这几个插件的坑
- Cursor AI 的 SFTP:传输速度慢得哭,尤其项目文件多一点,等得我花儿都谢了。
- Sublime Text 的 SFTP:要收费,虽说速度能快点,但不支持 rsync,上传的是整个文件,没法只传差异处,浪费流量和时间。
我的解决思路
为什么不自己写个脚本,用 rsync 来同步,只针对新增和修改的文件?而且还不被编辑器限制?
- 只上传改动的文件,大大节省时间和带宽
- 用
fswatch
实时监听文件变化,做到自动触发同步 - 支持配置排除目录,避免上传
.git
、node_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! 🚀