Created
November 17, 2025 15:51
-
-
Save jimmy947788/673c01f71b70e1ec20975d44f61a70ed to your computer and use it in GitHub Desktop.
move large folder by rsync
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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