Skip to content

Instantly share code, notes, and snippets.

@affix
Last active April 10, 2025 09:00
Show Gist options
  • Save affix/370b6b7abc6caff21d82b75186b3b362 to your computer and use it in GitHub Desktop.
Save affix/370b6b7abc6caff21d82b75186b3b362 to your computer and use it in GitHub Desktop.
A Bash script to do rclone bisync, I use this to sync proton drive on Linux but you can use it for anything rclone supports, This creates a systemd service to manage the sync process, You user must support linger enable with `sudo loginctl enable-linger $USER`
#!/bin/bash
# RCLONE_SYNC_PATH: The path to COPY FROM (files are not synced TO here):
RCLONE_SYNC_PATH="/home/${USER}/rclonedrive"
# RCLONE_BACKUP_PATH: Backup Directory
RCLONE_BACKUP_PATH="/backup/rclone"
# RCLONE_REMOTE: The rclone remote name to synchronize with.
RCLONE_REMOTE="remote:"
# RCLONE_CMD: The sync command and arguments:
RCLONE_CMD="rclone bisync ${RCLONE_SYNC_PATH} ${RCLONE_REMOTE} --force --backup-dir1 ${RCLONE_BACKUP_PATH} --verbose --recover"
# WATCH_EVENTS: The file events that inotifywait should watch for:
WATCH_EVENTS="modify,delete,create,move"
# SYNC_DELAY: Wait this many seconds after an event, before synchronizing:
SYNC_DELAY=5
# SYNC_INTERVAL: Wait this many seconds between forced synchronizations:
SYNC_INTERVAL=3600
# NOTIFY_ENABLE: Enable Desktop notifications
NOTIFY_ENABLE=true
# SYNC_SCRIPT: dynamic reference to the current script path
SYNC_SCRIPT=$(realpath $0)
notify() {
MESSAGE=$1
if test ${NOTIFY_ENABLE} = "true"; then
notify-send "rclone ${RCLONE_REMOTE}" "${MESSAGE}"
fi
}
# Copy a single file to remote
sync_single_file() {
local FILE_PATH="$1"
# Skip directories
if [[ -d "$FILE_PATH" ]]; then return; fi
# Get relative path from RCLONE_SYNC_PATH
REL_PATH="${FILE_PATH#$RCLONE_SYNC_PATH/}"
# Parent directory of the destination (in case it doesn't exist)
DEST_DIR=$(dirname "${RCLONE_REMOTE}${REL_PATH}")
echo "Uploading file: $REL_PATH"
# rclone copyto preserves destination filename, avoids directory auto-creation
rclone copyto "$FILE_PATH" "${RCLONE_REMOTE}${REL_PATH}" && \
notify "Copied: $REL_PATH"
}
rclone_sync() {
set -x
# Do initial sync immediately:
notify "Started rclone sync service"
${RCLONE_CMD}
# Watch for file events and do continuous immediate syncing
# and regular interval syncing:
while [[ true ]] ; do
EVENT=$(inotifywait --recursive --timeout ${SYNC_INTERVAL} -e ${WATCH_EVENTS} \
--format '%e %w%f' "${RCLONE_SYNC_PATH}" 2>/dev/null)
INOTIFY_EXIT=$?
if [ $INOTIFY_EXIT -eq 0 ]; then
EVENT_TYPE=$(echo "$EVENT" | awk '{print $1}')
FILE_PATH=$(echo "$EVENT" | cut -d' ' -f2-)
echo "Detected event: $EVENT_TYPE on $FILE_PATH"
sleep ${SYNC_DELAY}
case "$EVENT_TYPE" in
MODIFY|CREATE|CLOSE_WRITE|MOVED_TO)
sync_single_file "$FILE_PATH"
;;
DELETE|MOVED_FROM)
REL_PATH="${FILE_PATH#$RCLONE_SYNC_PATH/}"
echo "Deleting remote: ${REL_PATH}"
rclone deletefile "${RCLONE_REMOTE}${REL_PATH}" && \
notify "Deleted: $REL_PATH"
;;
*)
echo "Unhandled event: $EVENT_TYPE"
;;
esac
elif [ $? -eq 2 ]; then
# Do the sync now even though no changes were detected:
${RCLONE_CMD}
fi
done
}
systemd_setup() {
set -x
mkdir -p ${HOME}/.config/systemd/user
SERVICE_FILE=${HOME}/.config/systemd/user/rclone_sync.${RCLONE_REMOTE}.service
if test -f ${SERVICE_FILE}; then
echo "Unit file already exists: ${SERVICE_FILE} - Not overwriting."
else
cat <<EOF > ${SERVICE_FILE}
[Unit]
Description=rclone_sync ${RCLONE_REMOTE}
[Service]
ExecStart=${SYNC_SCRIPT}
[Install]
WantedBy=default.target
EOF
fi
systemctl --user daemon-reload
systemctl --user enable --now rclone_sync.${RCLONE_REMOTE}
systemctl --user status rclone_sync.${RCLONE_REMOTE}
echo "You can watch the logs with this command:"
echo " journalctl --user --unit rclone_sync.${RCLONE_REMOTE}"
}
if test $# = 0; then
rclone_sync
else
CMD=$1; shift;
${CMD} $@
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment