Skip to content

Instantly share code, notes, and snippets.

@deadjakk
Created November 28, 2023 22:08
Show Gist options
  • Save deadjakk/ded8b67d6443add26c98ad8acfb3000b to your computer and use it in GitHub Desktop.
Save deadjakk/ded8b67d6443add26c98ad8acfb3000b to your computer and use it in GitHub Desktop.
Simple Linux Backup Bash Script - backup.sh
#!/bin/bash
# Essentially this is a wrapper around rsync to provide context switching and non-relative syncing of files.
# it's a Keep It Simple Stupid backup utility.
# During init you define a root, a remote backup server, and a profile name.
# From there you just enter the same backup server and profile name on any other host to backup and restore files.
# What files are backed up are determined by what symlinks you define in the backup directory.
# These symlinks will be followed in reverse to restore files to their original location after being pulled.
# That's it.
##################### CONFIGURATION
command -v rsync 2>&1 >/dev/null
if [ ! $? = 0 ]; then
echo "rsync not found, this script needs it, exiting."
exit
fi
if [ -e $HOME/.backup_env ]; then
. $HOME/.backup_env
else
if [ ! $1 = "init" ]; then
echo "ERROR: $HOME/.backup_env not found, consider running \"$( basename $0 ) init\" to create it"
fi
fi
BACKUP_ROOT=${BACKUP_ROOT:-$HOME}
PROFILE_NAME=${PROFILE_NAME:-bak}
BACKUP_SERVER="${BACKUP_SERVER:-hermit}"
## add files you don't want copied in this var, it should be a space-separated list of files
EXCLUDE="chromium-bin"
RSYNCFLAGS="-avLP --update"
##################### CONFIGURATION END
# handle excluded files
EXCLUDEFLAGS=""
for file in $EXCLUDE; do
EXCLUDEFLAGS=" $EXCLUDEFLAGS --exclude=${file} "
done
RSYNCFLAGS="$RSYNCFLAGS $EXCLUDEFLAGS"
function push {
# push command (default)
echo backing up to $BACKUP_SERVER
rsync $RSYNCFLAGS ~/.${PROFILE_NAME} $BACKUP_SERVER:~/${PROFILE_NAME}
}
function help {
echo "$(basename $0) - SETUP AND PERFORM BACKUPS AND RESTORES"
echo "BACKUP ROOT -> $BACKUP_ROOT"
echo "BACKUP SERVER -> $BACKUP_SERVER"
echo "PROFILE -> $PROFILE_NAME"
echo
echo "init - setup environment and build initial directories where needed on both client and server"
echo "push - backup your system to $BACKUP_SERVER"
echo "pull - pull down the files for $PROFILE_NAME from $BACKUP_SERVER"
echo "fetch - pull down new symlinks for $PROFILE_NAME files from $BACKUP_SERVER"
echo "move - move files from the backup root to their original symlink targets"
echo "chenv - change env settings, useful for switching profiles"
echo
echo "set \$DEBUG variable to enable debug output"
}
# pulls metadata and such, not the actual files
# BOOKMARK
# TODO can we make fetch just get the symlink files using only rsync with a depth of 1? with --delete and --update?
# TODO write the logic for fixing the file paths after running pull
# TODO test the usage of --delete
function fetch {
if [ ! -d $BACKUP_ROOT/.${PROFILE_NAME} ]; then
mkdir -p $BACKUP_ROOT/.${PROFILE_NAME} && echo created symlink directory
fi
fnum=0
for file in $( ssh $BACKUP_SERVER "ls -A ~/${PROFILE_NAME}/.${PROFILE_NAME} 2>/dev/null" ); do
ln -s $BACKUP_ROOT/$file $BACKUP_ROOT/.${PROFILE_NAME}/ 2>/dev/null && echo created symbolic link for $file
fnum=$((fnum+1))
done
if [ $fnum = 0 ]; then
echo "error: could not pull file list from $BACKUP_SERVER"
return
fi
echo "review the symlinks within $BACKUP_ROOT/.${PROFILE_NAME} before doing your next pull"
}
function pull {
echo "restoring from ${BACKUP_SERVER}"
rsync $RSYNCFLAGS $BACKUP_SERVER:~/${PROFILE_NAME}/.${PROFILE_NAME}/* $BACKUP_ROOT/
rsync $RSYNCFLAGS $BACKUP_SERVER:~/${PROFILE_NAME}/.${PROFILE_NAME}/.??* $BACKUP_ROOT/
move_files
exit
}
function write_env {
DEFAULT=$PROFILE_NAME
echo -n "enter or create desired profile name [$DEFAULT]: "
read PROFILE_NAME
PROFILE_NAME=${PROFILE_NAME:-$DEFAULT}
echo "export PROFILE_NAME=$PROFILE_NAME" > $HOME/.backup_env
DEFAULT=$BACKUP_SERVER
echo
echo -n "enter hostname, ip, or ssh config Hostname for backup server [$DEFAULT]: "
read BACKUP_SERVER
BACKUP_SERVER=${BACKUP_SERVER:-$DEFAULT}
echo "export BACKUP_SERVER=$BACKUP_SERVER" >> $HOME/.backup_env
DEFAULT=$BACKUP_ROOT
echo
echo "enter the path to use as the backup root (where all files get copied before being moved)"
echo -n "this is also where the hidden symlink directory is kept [$DEFAULT]: "
read BACKUP_SERVER
BACKUP_ROOT=${BACKUP_ROOT:-$DEFAULT}
echo "export BACKUP_ROOT=$BACKUP_ROOT" >> $HOME/.backup_env
echo
echo "finished writing $HOME/.backup_env"
. $HOME/.backup_env
}
function init_server {
ssh $BACKUP_SERVER "mkdir -p ~/${PROFILE_NAME}/.${PROFILE_NAME} 2>/dev/null" &&\
echo "created $HOME/${PROFILE_NAME}/.${PROFILE_NAME} on $BACKUP_SERVER if it was not there."
# echo "any time you add files to this directory you need to rerun the 'init' function of the backup script to populate the new files as symbolic links"
}
function debug_echo {
if [ ! -z $DEBUG ]; then
echo $@
fi
}
function move_files {
## this moves files from the BACKUP_ROOT to their final destination
# use readlink to read where the individual files came from and then
# put them back there based on what is present in
# the client's BACKUP_ROOT
for link in $( ls -A $BACKUP_ROOT/.${PROFILE_NAME} | grep -vE "^__noauto" ); do
if [[ ! -e "$BACKUP_ROOT/$link" ]]; then
debug_echo "$BACKUP_ROOT/$link does not exist, skipping"
continue
fi
target=$( readlink -f $BACKUP_ROOT/.${PROFILE_NAME}/$link )
debug_echo "$BACKUP_ROOT/$link = $target"
if [[ ! "$BACKUP_ROOT/$link" = "$target" ]]; then
debug_echo "moving $link"
if [[ -e $target ]]; then
mv $BACKUP_ROOT/$link $target
rm -rf $BACKUP_ROOT/$link
else
echo "$target does not exist. please create it and re-run the pull or move command"
fi
fi
done
}
function init {
write_env
init_server
fetch
echo
echo "TO USE:"
echo "create symlinks to files you wish to backup inside of $BACKUP_ROOT/.${PROFILE_NAME}"
echo "you can also add files you wish to backup/distribute to $BACKUP_SERVER:$BACKUP_ROOT/${PROFILE_NAME}/.${PROFILE_NAME}"
echo
echo "you can use fetch to create new symlinks from the server at any time,this is just for convenience you will"
echo "still get the files from the server, they will just be dropped in your root directory and will not get backed"
echo "up until you create the symlinks in your own directory."
echo
echo "see \"$( basename $0 ) help\" for other usage details and specific commands"
exit
}
if [ -z $1 ] || [[ $1 = "-h" ]] || [[ $1 = "help" ]]; then
help
exit
fi
if [[ $1 = "move" ]]; then
move_files
exit
fi
if [[ $1 = "fetch" ]]; then
fetch
exit
fi
if [[ $1 = "pull" ]]; then
pull
exit
fi
if [[ $1 = "init" ]]; then
init
exit
fi
if [[ $1 = "chenv" ]]; then
write_env
exit
fi
# PUSH function, default script function
if [ $1 = "push" ]; then
push
exit
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment