cloudup: Sync với cloud drive (II)

Khi ta định kỳ sync một thư mục local với một thư mục remote, thí dụ Google Drive hay Onedrive, trở ngại lớn nhất là kích thước thư mục. Thời gian kiểm tra để biết file/thư mục nào mới cần tải lên trở nên khá lâu, gần 30 phút với thư mục 100GB và còn tùy ở băng thông upload.

Có một cách khác là giám sát thư mục local, chỉ upload file/thư mục con mới/có thay đổi của nó mà thôi. Việc giám sát này cần inotifywait trong gói inotify-tools.

inotifywait -mr --event 'create,close_write,delete,move' --format "%e %w%f" "$LOCAL_DIR"

Câu lệnh trên cho phép inotify giám sát thư mục $LOCAL_DIR trên các sự kiện create, close_write, delete, move của file và thư mục. Khi có sự kiện này trên thư mục được giám sát, inotifywait báo cho chúng ta tên sự kiện và filename. Tất nhiên khi đó chúng ta lại gọi rclone có hành động tương ứng.

#!/bin/bash
# Script cloudup, version 20220515
# © 2019 LNT <lnt@ly-le.info>
# update all folder changes to the cloud drive
N=$(basename $0)
LF="/run/$N.pid"
M=$(mktemp /dev/shm/${N}-XXXX)
L=$HOME/$N.log
trap 'sudo rm -f "$LF";rm -rf $M' EXIT
echo -e "DATE:$(date)\nPID:$$"|sudo tee "$LF">/dev/null

function usage() {
  cat >&2 <<EOF
Automatically update all folder changes to the cloud drive

usage: $N [-v][-x file|folder][-o log] LOCAL_DIR REMOTE_DIR
  x: exclude file or folder
  o: log filename, default $L
  v: verbose

EOF
  [ $# -ne 0 ] && echo -e "Error: $1!"
  exit 2
}

O=('--transfers=20' '--checkers=300' '--tpslimit=20' '--drive-acknowledge-abuse' '--fast-list' '-u')
X=()
while getopts :vo:x opt:; do
  case $opt in
  o)
    [[ ! "$L" = "$OPTARG" ]] && { [ -f $L ] && { cat "$L">>"$OPTARG";rm -f "$L"; }; L=">
    ;;
  x)
    X+=("$OPTARG")
    ;;
  v)
    O+=('-v')
    ;;
  ?)
    usage
    ;;
  esac
done
shift $(( $OPTIND-1 ))

[ $# -ne 2 ] && usage "LOCAL_DIR or REMOTE_DIR is invalid"
LD="$1"
RD="$2"
if [ ! "${LD: -1}" = '/' ]; then
  RD=$RD/$(basename $LD)
  LD=${LD}/
fi
[[ ! "${RD: -1}" = ':' &&  ! "${RD: -1}" = '/' ]] && RD=${RD}/

if ((${#X[@]})); then
  for (( i=0; i<${#X[@]}; i++ ));do
    [[ ! "${X[$i]}" =~ ^"$LD" ]] && X[$i]=$LD${X[$i]}
  done
fi
sudo sysctl fs.inotify.max_user_watches=524288 &> /dev/null
sudo sysctl -p &> /dev/null
O+=("--log-file=$L")
O=${O[@]}
inotifywait -m -r -e CREATE,CLOSE_WRITE,DELETE,MOVED_FROM,MOVED_TO,MOVE_SELF --format "%e %w%f" $LD>
  while read e t; do
  r=$RD${t#"$LD"}
  c=0
  for p in ${X[@]};do
    [[ "$t" =~ ^"$p" ]] && { c=1;break; }
  done
  (( $c )) && continue

  date +"%Y/%m/%d %T INFO  : event=$e, local=$t, remote=$r" | tee -a "$L"
  case $e in
  CLOSE_WRITE,CLOSE)
    rclone copyto "$t" "$r" $O
    ;;
  CREATE,ISDIR)
    [ -z "$(ls -A "$t")" ]&&rclone mkdir "$r" $O || rclone copyto "$t" "$r" $O
    ;;
  DELETE)
    rclone delete "$r" $O
    ;;
  DELETE,ISDIR)
    rclone purge "$r" $O
    ;;
  MOVED_FROM|MOVED_FROM,ISDIR)
    echo "$r" > $M
    ;;
  MOVED_TO | MOVED_TO,ISDIR)
    o=$(<$M) && >$M
    rclone moveto "$o" "$r" $O
    ;;
  MOVE_SELF) #folder only
    ofile=$(<$M) && >$M
    [ -z "$o" ] || rclone purge "$r" $O
    ;;
    esac
  done			

Script cloudup được gọi thường trú mỗi khi RPi khởi động

@reboot /path/to/cloudup local_folder remote:folder

Chú thích

  1. Script copy/sync thư mục con cuối cùng của local_folder sang remote, /path/to/folder -> remote:folder
    Đề copy/sync local_folder sang thư mục khác tên, dùng / ở cuối, /path/to/folder/ -> remote:/new_folder
  2. Rclone hỗ trợ symbolic link nhưng inotifywait thì không. File trong thư mục đích của symlink có thay đổi nhưng symlink vẫn không thay đổi nên inotifywait không phát hiện, do vậy script không cập nhật lên REMOTE_DIR.
  3. Tuy nhiên, script có thể được cải tiến để hỗ trợ symlink bằng cách giám sát cả thư mục đích của mọi symlink trong thư mục local (đã xong!)
  4. Script dùng cho các ổ đĩa đám mây được rclone hỗ trợ.

Comments Off on cloudup: Sync với cloud drive (II)

Filed under Software

Comments are closed.