#!/bin/bash
# ============================================================
# sync-upload.sh —— 本地目录同步到服务器(全量覆盖)
#
# 只需要改下面「配置区」的 5 个变量,然后双击/命令行运行即可。
#
# 用法:
# Git Bash 终端: bash sync-upload.sh
# 或(Windows 资源管理器里双击 .bat 也会自动调起,见 sync-upload.bat)
#
# 原理(为什么这样做):
# - scp 直接传目录会在服务器多套一层、不支持排除、且不删服务器端多余文件;
# - 本脚本用 tar 本地打包(支持 --exclude 排除)→ scp 传单个 tar 包 →
# 服务器端解压到临时目录 → mv 原子替换目标目录,实现真正的“本地覆盖服务器”。
# ============================================================
set -e
# ============================================================
# 配置区 —— 只需要填这里
# ============================================================
HOST="120.77.213.195" # 例:192.168.1.10 或 myserver.com
PORT="22" # SSH 端口,默认 22
USER="root" # 登录用户名
LOCAL_DIR="/d/work/other/work-buddy/homeWork" # 本地目录(绝对路径,Windows 盘符用 /d/xxx 格式)
REMOTE_DIR="/www/wwwroot/default/tx/homeWork" # 服务器目标目录(绝对路径)
# 需要排除的目录/文件(相对本地目录),留空则不排除
EXCLUDES=(
".git"
"node_modules"
"__pycache__"
"*.pyc"
".workbuddy"
".gitignore"
)
# ============================================================
# 脚本逻辑(一般无需改动)
# ============================================================
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
echo -e "${GREEN}===== 目录同步(本地 → 服务器,全量覆盖)=====${NC}"
# 校验配置
if [[ "$HOST" == *"你的服务器"* ]]; then
echo -e "${RED}[错误] 请先编辑脚本,填写 HOST / USER / LOCAL_DIR / REMOTE_DIR${NC}"
exit 1
fi
if [[ ! -d "$LOCAL_DIR" ]]; then
echo -e "${RED}[错误] 本地目录不存在: $LOCAL_DIR${NC}"
exit 1
fi
# 组装 tar 的排除参数(tar 支持 --exclude,语义与 rsync 一致)
TAR_EXCL_ARGS=()
for e in "${EXCLUDES[@]}"; do
[[ -n "$e" ]] && TAR_EXCL_ARGS+=(--exclude "$e")
done
# SSH 基础参数(免密登录,无需连接复用——Windows 下 ControlMaster 易触发
# "read from master failed" 报错,直接每次独立连接反而更稳定)
SSH_COMMON=(-p "$PORT" -o ConnectTimeout=15)
# 服务器上的临时目录(放在目标目录同级的隐藏目录,保证同一文件系统,mv 才能原子)
TMP_DIR="${REMOTE_DIR}.tmp_$$"
echo -e "本地目录 : ${YELLOW}$LOCAL_DIR${NC}"
echo -e "目标 : ${YELLOW}${USER}@${HOST}:${REMOTE_DIR}${NC} (端口 $PORT)"
echo -e "排除 : ${EXCLUDES[*]:-无}"
echo ""
# 先建立 SSH 连接(已配置免密,直接连;复用连接加速后续步骤),顺便确认目录存在
echo -e "${YELLOW}[1/3] 连接服务器(免密登录)...${NC}"
ssh "${SSH_COMMON[@]}" "${USER}@${HOST}" "echo '连接成功,远端用户: \$(whoami)'"
echo ""
# 确认
read -r -p "确认将 [$LOCAL_DIR] 全量覆盖到服务器 [$REMOTE_DIR] ? (y/n): " ans
if [[ "$ans" != "y" && "$ans" != "Y" ]]; then
echo "已取消。"
exit 0
fi
# 上传到临时目录
echo -e "${YELLOW}[2/3] 打包并上传文件...${NC}"
# 1) 本地 tar 打包(支持 --exclude 排除),打包成单个 .tar.gz
TAR_NAME="sync_$$.tar.gz"
TAR_LOCAL="/tmp/${TAR_NAME}"
tar -czf "$TAR_LOCAL" "${TAR_EXCL_ARGS[@]}" -C "$LOCAL_DIR" .
# 2) scp 上传单个压缩包(先传到服务器 /tmp)
scp -P "$PORT" -o ConnectTimeout=15 "$TAR_LOCAL" "${USER}@${HOST}:/tmp/${TAR_NAME}"
# 3) 本地清理临时包
rm -f "$TAR_LOCAL"
# 4) 服务器端解压到临时目录
ssh "${SSH_COMMON[@]}" "${USER}@${HOST}" "
mkdir -p '$TMP_DIR' &&
tar -xzf /tmp/${TAR_NAME} -C '$TMP_DIR' &&
rm -f /tmp/${TAR_NAME} &&
echo '解压完成'
"
# 服务器端原子替换:备份旧的,把临时目录换成正式目录
echo -e "${YELLOW}[3/3] 服务器端替换目录...${NC}"
ssh "${SSH_COMMON[@]}" "${USER}@${HOST}" "
if [ -e '$REMOTE_DIR' ]; then
rm -rf '${REMOTE_DIR}.bak_$$'
mv '$REMOTE_DIR' '${REMOTE_DIR}.bak_$$'
fi
mv '$TMP_DIR' '$REMOTE_DIR' && echo '替换完成,旧目录已备份为 ${REMOTE_DIR}.bak_$$'
"
echo ""
echo -e "${GREEN}===== 同步完成 =====${NC}"
echo -e "服务器目录: ${YELLOW}${USER}@${HOST}:${REMOTE_DIR}${NC}"
echo -e "旧版本备份: ${REMOTE_DIR}.bak_$$(确认无误后可手动删除)"