Created
November 28, 2023 22:08
-
-
Save deadjakk/ded8b67d6443add26c98ad8acfb3000b to your computer and use it in GitHub Desktop.
Simple Linux Backup Bash Script - backup.sh
This file contains 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
#!/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