#!/usr/bin/env bash ## Back up my files to each of my external drives. ## ## Usage: backup ## ## Dependencies: GNU coreutils, GNU grep, GNU sed, rsync set -euo pipefail shopt -s inherit_errexit die() { echo -e "$(basename "$0"): $1" >&2; exit 1; } [[ " $* " =~ ' --help ' ]] && sed -n 's/^## *//p' "$0" && exit (( UID != 0 )) || die 'do not run as root' sources=( ~/./.archive ~/./.bashrc ~/./.config/.archive ~/./.config/Xresources ~/./.config/chromium/Default/Bookmarks ~/./.config/chromium/Default/Custom\ Dictionary.txt ~/./.config/dunst ~/./.config/emacs/.archive ~/./.config/emacs/init.el ~/./.config/emacs/lisp ~/./.config/emacs/themes ~/./.config/fontconfig ~/./.config/git ~/./.config/gpick/README ~/./.config/gthumb/README ~/./.config/gtk-3.0 ~/./.config/gtk-4.0 ~/./.config/mpv ~/./.config/openbox ~/./.config/pip ~/./.config/procps/README ~/./.config/shellcheckrc ~/./.config/user-dirs.dirs ~/./.config/zathura ~/./.gnupg ~/./.ignore ~/./.local/share/.archive ~/./.local/share/password-store ~/./.local/share/selectcmd.dat ~/./.local/share/systemd/user ~/./.local/share/zathura ~/./.mozilla/firefox/j8lriz5r.4/.archive ~/./.mozilla/firefox/j8lriz5r.4/bookmarkbackups ~/./.mozilla/firefox/j8lriz5r.4/bookmarks.html ~/./.mozilla/firefox/j8lriz5r.4/chrome ~/./.mozilla/firefox/j8lriz5r.4/persdict.dat ~/./.mozilla/firefox/j8lriz5r.4/sessionstore-backups ~/./.mozilla/firefox/j8lriz5r.4/user.js ~/./.npmrc ~/./.ssh ~/./.xinitrc ~/./main ) exclusions=( --exclude='*.asc' # OpenPGP keys exported for temporary use. --exclude='*.log' --exclude='*.log.*' # foo.log.2, foo.log.old, etc. --exclude='*.sqlite-wal' # Write-ahead log files. --exclude='.build/*' --exclude='.next/*' # Build directory for Next.js apps. --exclude='.wrangler/*' # Temporary files for projects that use Wrangler. --exclude='__pycache__/*' --exclude='build/*' # Build directory for Remix apps. --exclude='dist/*' # Build directory for Astro projects. --exclude='joshscalisi.com/src/dotfiles/*' --exclude='joshscalisi.com/src/scripts/*' --exclude='joshscalisi.com/vendor/*' --exclude='node_modules/*' ) destinations=( /media/wd-25a2-2tb/backups /media/wd-25e2-2tb-1/backups /media/wd-25e2-2tb-2/backups ) # Create backups. for dest in "${destinations[@]}"; do [[ -d $dest ]] || die "destination not found: $dest" latest_backup=$dest/latest new_backup=$dest/$(date +%Y/%m/%d%H%M%S) rsync_opts=(-vaRHAXh --mkpath "${exclusions[@]}") # Enable the creation of hard links to unchanged files if there is a previous # backup in the destination directory. if [[ -d $latest_backup ]]; then rsync_opts+=(--link-dest="$latest_backup") else echo -e "\nPrevious backup not found: $latest_backup" read -rp 'Create a new full backup without hard links? ' input [[ $input =~ ^[Yy] ]] && break fi echo -e "\nCreating backup in $new_backup" rsync "${rsync_opts[@]}" "${sources[@]}" "$new_backup" | # Don't print blank lines, directories, or Git files. grep -ve '^$' -e '/$' -e '/.git/' ln -fns "$new_backup" "$latest_backup" done