Skip to content

Instantly share code, notes, and snippets.

@jimmy947788
Created November 17, 2025 15:51
Show Gist options
  • Select an option

  • Save jimmy947788/673c01f71b70e1ec20975d44f61a70ed to your computer and use it in GitHub Desktop.

Select an option

Save jimmy947788/673c01f71b70e1ec20975d44f61a70ed to your computer and use it in GitHub Desktop.
move large folder by rsync
#!/usr/bin/env bash
set -euo pipefail
# ====== 使用說明 ======
# 本腳本支援本地同步和遠端同步兩種模式
#
# 本地同步(移動):
# ./rsync-move.sh local "/source/path/" "/dest/path/"
#
# 遠端同步(複製):
# ./rsync-move.sh remote "user@host:/source/path/" "/local/dest/"
# ./rsync-move.sh remote "/local/source/" "user@host:/remote/dest/"
#
# 不帶參數則使用下方預設設定
# ====================
# ====== 預設設定區 ======
DEFAULT_MODE="local" # 預設模式:local(移動)或 remote(複製)
DEFAULT_SRC="/path/src/" # 預設來源目錄(結尾要有 / 才表示搬內容)
DEFAULT_DST="/path/dst/" # 預設目標目錄(會自動建立)
LOG_DIR="./rsync-move-logs" # 紀錄檔路徑
SSH_KEY="" # SSH 金鑰路徑(選用,遠端同步時使用)
SSH_PORT="22" # SSH 連接埠(預設 22)
# ======================
# ====== 參數處理 ======
show_usage() {
echo "用法:"
echo " $0 # 使用預設設定"
echo " $0 local <來源路徑> <目標路徑> # 本地同步(移動)"
echo " $0 remote <來源路徑> <目標路徑> # 遠端同步(複製)"
echo ""
echo "範例:"
echo " $0 local \"/source/\" \"/dest/\" # 本地移動"
echo " $0 remote \"user@host:/src/\" \"/dest/\" # 從遠端複製到本地"
echo " $0 remote \"/src/\" \"user@host:/dest/\" # 從本地複製到遠端"
exit 1
}
# 解析命令列參數
if [[ $# -eq 0 ]]; then
# 無參數,使用預設值
MODE="$DEFAULT_MODE"
SRC="$DEFAULT_SRC"
DST="$DEFAULT_DST"
elif [[ $# -eq 3 ]]; then
MODE="$1"
SRC="$2"
DST="$3"
# 檢查模式是否有效
if [[ "$MODE" != "local" && "$MODE" != "remote" ]]; then
echo "錯誤:模式必須是 'local' 或 'remote'"
show_usage
fi
else
echo "錯誤:參數數量不正確"
show_usage
fi
# 建立必要目錄
mkdir -p "$LOG_DIR"
# 對於本地模式,確保目標目錄存在
if [[ "$MODE" == "local" ]]; then
mkdir -p "$DST"
fi
# 讓本地路徑結尾一定有斜線(保險)
if [[ "$SRC" != *:* ]]; then
[[ "${SRC: -1}" == "/" ]] || SRC="${SRC}/"
fi
if [[ "$DST" != *:* ]]; then
[[ "${DST: -1}" == "/" ]] || DST="${DST}/"
fi
TS="$(date '+%Y-%m-%d_%H-%M-%S')"
LOG_FILE="${LOG_DIR}/${MODE}_${TS}.log"
TMP_OUT="$(mktemp)"
echo "=== $(date '+%F %T') 開始 ${MODE} 同步 ===" | tee -a "$LOG_FILE"
echo "模式: $MODE" | tee -a "$LOG_FILE"
echo "SRC: $SRC" | tee -a "$LOG_FILE"
echo "DST: $DST" | tee -a "$LOG_FILE"
# 準備 SSH 選項(用於遠端同步)
SSH_OPTIONS=""
if [[ "$MODE" == "remote" ]]; then
SSH_OPTIONS="-e ssh"
if [[ -n "$SSH_KEY" ]]; then
SSH_OPTIONS="-e 'ssh -i $SSH_KEY -p $SSH_PORT'"
elif [[ "$SSH_PORT" != "22" ]]; then
SSH_OPTIONS="-e 'ssh -p $SSH_PORT'"
fi
fi
# 先統計來源端檔案數量與大小(本地路徑才能統計)
if [[ "$SRC" != *:* ]]; then
before_files=$(find "$SRC" -type f | wc -l || true)
before_size_human=$(du -sh --apparent-size "$SRC" 2>/dev/null | awk '{print $1}')
echo "來源目前檔案數:$before_files,目測容量:$before_size_human" | tee -a "$LOG_FILE"
else
echo "來源為遠端路徑,無法預先統計檔案數量" | tee -a "$LOG_FILE"
before_files="unknown"
fi
echo ">> 開始 rsync(顯示整體進度,失敗可續傳)…" | tee -a "$LOG_FILE"
# 根據模式選擇 rsync 選項
RSYNC_OPTS="-av --info=progress2 --stats --partial --inplace --human-readable"
if [[ "$MODE" == "local" ]]; then
# 本地模式:移動檔案(刪除來源)
RSYNC_OPTS="$RSYNC_OPTS --remove-source-files --prune-empty-dirs"
echo ">> 本地移動模式:將會刪除來源檔案" | tee -a "$LOG_FILE"
elif [[ "$MODE" == "remote" ]]; then
# 遠端模式:複製檔案(保留來源)
echo ">> 遠端複製模式:保留來源檔案" | tee -a "$LOG_FILE"
if [[ -n "$SSH_OPTIONS" ]]; then
RSYNC_OPTS="$RSYNC_OPTS $SSH_OPTIONS"
fi
fi
# 執行 rsync
echo "執行指令:rsync $RSYNC_OPTS \"$SRC\" \"$DST\"" | tee -a "$LOG_FILE"
# 設定環境變數並執行 rsync
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
if [[ "$MODE" == "remote" && -n "$SSH_OPTIONS" ]]; then
# 遠端模式且有 SSH 選項時,需要特殊處理
eval "rsync $RSYNC_OPTS \"$SRC\" \"$DST\"" 2>&1 | tee -a "$LOG_FILE" | tee "$TMP_OUT" >/dev/null
else
# 直接執行 rsync
rsync $RSYNC_OPTS "$SRC" "$DST" 2>&1 | tee -a "$LOG_FILE" | tee "$TMP_OUT" >/dev/null
fi
# 本地模式才需要清理空資料夾和統計剩餘檔案
if [[ "$MODE" == "local" && "$SRC" != *:* ]]; then
echo ">> 清理來源端空資料夾…" | tee -a "$LOG_FILE"
find "$SRC" -type d -empty -delete || true
# 搬運後統計
after_files=$(find "$SRC" -type f | wc -l || true)
if [[ "$before_files" != "unknown" ]]; then
moved_files=$(( before_files - after_files ))
else
moved_files="unknown"
fi
else
after_files="N/A"
moved_files="N/A"
fi
# 從 rsync --stats 解析更精準的統計
files_transferred=$(awk -F': ' '/Number of regular files transferred/ {print $2}' "$TMP_OUT")
bytes_transferred=$(awk -F': ' '/Total transferred file size/ {print $2}' "$TMP_OUT")
echo "=== $(date '+%F %T') 完成 ${MODE} 同步 ===" | tee -a "$LOG_FILE"
if [[ "$MODE" == "local" ]]; then
echo "搬移檔案數(估):$moved_files / 原本 $before_files" | tee -a "$LOG_FILE"
else
echo "遠端同步模式:檔案已複製,來源檔案保持不變" | tee -a "$LOG_FILE"
fi
[[ -n "${files_transferred:-}" ]] && echo "rsync 報告已傳檔案數:$files_transferred" | tee -a "$LOG_FILE"
[[ -n "${bytes_transferred:-}" ]] && echo "rsync 報告傳輸總量:$bytes_transferred" | tee -a "$LOG_FILE"
# 本地模式才檢查殘留檔案
if [[ "$MODE" == "local" && "$after_files" != "N/A" && "$after_files" -gt 0 ]]; then
echo "⚠️ 來源端仍有 $after_files 個檔案未搬移/未刪除,請檢查權限或是否被占用。" | tee -a "$LOG_FILE"
fi
echo "LOG 已寫入:$LOG_FILE"
rm -f "$TMP_OUT"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment