backupuser

Creating backups is an essential part of a users routine operations. Here are two scripts that a user can use, to manage backups of his home folder ~/:


backupuser


#!/bin/sh

# Copyright (c) 2015-2020 Oliver Mahmoudi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions 
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

### backupuser - a user backup utility

# Functions:
# make_package_list
# create_backup
# backup_etc
# do_hook
# make_clean
# strip_trailing_slash
# scp_get_args
# scp_get_destination
# copy_backup_scp
# get_path_to_file
# get_filename
# delete_backup
# usage

# Variables:
WHO=`whoami`		# == WHO=$(whoami)
DATE=$(date +%m.%d.%Y)
YEAR=$(date +%Y)	# This year
TIME=$(date +%H-%M-%S)
FILENAME=${WHO}_${DATE}_${TIME}.tar.gz
ETC_NAME=etc_${DATE}_${TIME}.tar.gz
USER_REPORT_FILE=${WHO}_report
ETC_REPORT_FILE=etc_report
BACKUP_BASE=${HOME}/backups
BACKUP_DIR=${BACKUP_BASE}/${YEAR}
BACKUP_DIR_ETC=${BACKUP_BASE}/etc/${YEAR}
BU_CONTENTS=		# contents of ${HOME}/.bu
SCP_DEST=
h_flag=0			# create backup in ${HOME}
j_flag=0			# ignore pre-backup processing
k_flag=0			# ignore post-backup processing
p_flag=0			# create installed packages list
r_flag=0			# do not log backup in user_report_file
s_flag=0			# scp flag
v_flag=0			# verbose flag
x_flag=0			# do not process ~/.bu/bu_excludes

#
# Save a list of the installed packages in root's home direcory if desired.
# Different operating systems use different package managers. 
# Currently supported are: Linux's yum, dpkg and FreeBSD's pkg.
#
function make_package_list()
{
	if [ -e "/usr/bin/yum" ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo -n "Generating installed packages list..."
		fi
		yum list installed > ~/yum_list_installed
		if [ $v_flag -eq 1 ] ; then
			echo " done."
			echo -n "Generating installed groups list..."
		fi
		yum grouplist > ~/yum_grouplist
		if [ $v_flag -eq 1 ] ; then
			echo " done."
			echo -n "Generating installed repos list..."
		fi
		yum repolist all > ~/yum_repolist_all
		if [ $v_flag -eq 1 ] ; then
			echo " done."
			echo
		fi
	elif [ -e "/usr/bin/dpkg" ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo -n "Generating installed packages list..."
		fi
		dpkg -l > ~/installed_packages_list
		if [ $v_flag -eq 1 ] ; then
			echo " done."
		fi
	else
		echo "Unknown package manager."
	fi
}

#
# Backup the user's home directoy. This is the main function.
#
function create_backup()
{
	if [ $v_flag -eq 1 ] ; then
		echo "Getting ready to backup ${WHO}'s home directory:"
	fi

	if [ "${WHO}" = "root" -a $p_flag -eq 1 ] ; then
		make_package_list
	fi

	if [ ! -d ${BACKUP_DIR} ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo -n "Your backup directory: ${BACKUP_DIR} doesn't seem to exist. Creating it..."
		fi
		mkdir -p ${BACKUP_DIR}
		if [ $v_flag -eq 1 ] ; then
			echo " done."
		fi
	fi

	# Invoke the do_hook function which checks for any extra files, that begin with
	# any prefix other than the word 'post', in ${HOME}/.bu before the actual backup.
	# The file bu_excludes, if found, will not be sourced.
	if [ $j_flag -eq 0 ] ; then
		do_hook "pre"
	fi

	# Move to the temporary backup directory /tmp or else ${BACKUP_DIR} if h_flag is set
	if [ $v_flag -eq 1 -a $h_flag -eq 0 ] ; then
		echo -n "Moving to temporary backup folder /tmp..."
	elif [ $v_flag -eq 1 -a $h_flag -eq 1 ] ; then
		echo -n "Moving to backup folder ${BACKUP_DIR}..."
	fi

	if [ $h_flag -eq 0 ] ; then
		cd /tmp > /dev/null 2>&1
	else
		cd ${BACKUP_DIR} > /dev/null 2>&1
	fi

	if [ $v_flag -eq 1 ] ; then
		echo " done."
	fi

	# Create the backup
	if [ -e "${HOME}/.bu/bu_excludes" -a $x_flag -eq 0 ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo "The following files and folders will not be backed up:"
			cat ${HOME}/.bu/bu_excludes
			echo
			if [ $h_flag -eq 0 ] ; then
				echo -n "Creating backup in /tmp..."
			else
				echo -n "Creating backup in ${BACKUP_DIR}..."
			fi
		fi
		tar -czf ${FILENAME} --exclude ${BACKUP_BASE} -X ${HOME}/.bu/bu_excludes ${HOME} > /dev/null 2>&1
		if [ $v_flag -eq 1 ] ; then
			echo " done."
		fi
	else
		if [ $v_flag -eq 1 -a $h_flag -eq 0 ] ; then
			echo -n "Creating backup in /tmp..."
		elif [ $v_flag -eq 1 -a $h_flag -eq 1 ] ; then
			echo -n "Creating backup in ${BACKUP_DIR}..."
		fi
		tar -czf ${FILENAME} --exclude ${BACKUP_BASE} ${HOME} > /dev/null 2>&1
		if [ $v_flag -eq 1 ] ; then
			echo " done."
		fi
	fi

	# If $h_flag == 0, move the backup to the directory it is destined for, ${BACKUP_DIR}
	# When being verbose, we here only need to report "echo done" to the user, when
	# we are compiling the archive in /tmp.
	if [ $v_flag -eq 1 -a $h_flag -eq 0 ] ; then
		echo -n "Moving backup from /tmp to ${BACKUP_DIR}..."
	fi

	if [ $h_flag -eq 0 ] ; then
		mv ${FILENAME} ${BACKUP_DIR} > /dev/null 2>&1
	fi

	if [ $v_flag -eq 1 -a $h_flag -eq 0 ] ; then
		echo " done."
	fi

	# In case $h_flag == 1, we are already there, so therefore...
	if [ $h_flag -eq 0 ] ; then
		cd ${BACKUP_DIR} > /dev/null 2>&1
	fi

	# Test the backup
	if [ $v_flag -eq 1 ] ; then
		echo -n "Testing the backup..."
	fi
	gzip --test ${FILENAME} > /dev/null 2>&1

	if [ $? -eq 0 ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo ' OK!'
		fi
	else
		echo "File: ${FILENAME} is corrupted."
		rm -v ${FILENAME}
		exit 1
	fi

	# Checksums
	if [ $r_flag -eq 0 ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo -n "Generating checksum in ${BACKUP_DIR}/${USER_REPORT_FILE}..."
		fi
		sha256sum ${FILENAME} >> ${USER_REPORT_FILE}
		if [ $v_flag -eq 1 ] ; then
			echo " done."
		fi
	fi

	# Call the do_hook function which checks for and sources any file, that
	# begins with the word 'post' to process in ${HOME}/.bu after the backup.
	if [ $k_flag -eq 0 ] ; then
		do_hook "post"
	fi

	# Print out a status report.
	echo 'Backup successful!'
	echo "Your backup file is: ${BACKUP_DIR}/${FILENAME}"
}

#
# Make a backup of the /etc directory.
#
function backup_etc()
{
	if [ "${WHO}" != "root" ] ; then
		echo 'You need to be root to backup the /etc directory!'
		exit 1
	fi

	if [ $v_flag -eq 1 ] ; then
		echo "Getting ready to backup the /etc directory:"
	fi

	if [ ! -d ${BACKUP_DIR_ETC} ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo -n "Backup directory: ${BACKUP_DIR_ETC} doesn't seem to exist. Creating it..."
		fi
		mkdir -p ${BACKUP_DIR_ETC}
		if [ $v_flag -eq 1 ] ; then
			echo " done."
		fi
	fi

	# Move to the temporary backup directory /tmp. Only root can initiate a backup of
	# /etc so /tmp is always available.
	if [ $v_flag -eq 1 ] ; then
		echo -n "Moving to temporary backup folder /tmp..."
	fi
	cd /tmp > /dev/null 2>&1
	if [ $v_flag -eq 1 ] ; then
		echo " done."
	fi

	if [ $v_flag -eq 1 ] ; then
		echo -n "Creating the backup..."
	fi
	tar -czf ${ETC_NAME} /etc > /dev/null 2>&1
	if [ $v_flag -eq 1 ] ; then
		echo " done."
	fi

	# Move the backup to the directory it is destined for
	if [ $v_flag -eq 1 ] ; then
		echo -n "Moving backup from /tmp to ${BACKUP_DIR_ETC}..."
	fi
	mv ${ETC_NAME} ${BACKUP_DIR_ETC} > /dev/null 2>&1
	if [ $v_flag -eq 1 ] ; then
		echo " done."
	fi
	cd ${BACKUP_DIR_ETC} > /dev/null 2>&1

	# test the backup
	if [ $v_flag -eq 1 ] ; then
		echo -n "Testing the backup..."
	fi
	gzip -t ${ETC_NAME} > /dev/null 2>&1

	if [ $? -eq 0 ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo ' OK!'
		fi
	else
		echo "File: ${ETC_NAME} is corrupted."
		rm -v ${ETC_NAME}
		exit 1
	fi
		
	# Checksums
	if [ $r_flag -eq 0 ] ; then
		if [ $v_flag -eq 1 ] ; then
			echo -n "Generating checksums in ${BACKUP_DIR_ETC}/${ETC_REPORT_FILE}..."
		fi
		sha256sum ${ETC_NAME} >> ${ETC_REPORT_FILE}
        if [ $v_flag -eq 1 ] ; then
                echo " done."
        fi
	fi

	# Print out a status report.
	echo 'Backup successful!'
	echo "/etc backup file is: ${BACKUP_DIR_ETC}/${ETC_NAME}"
}

#
# The modular hook function begings here. Check whether there are any files in the
# $HOME/.bu folder. If so, then source them.
#
function do_hook()
{
	if [ ! -d ${HOME}/.bu ] ; then
		return
	fi

	# Pre-Backup stage: Process any file, that begins with a prefix other then 'post'
	# and is not the bu_excludes file.
	if [ "$1" = "pre" ] ; then
		cd $HOME/.bu
		BU_CONTENTS=*
		if [ -n "$BU_CONTENTS" ] ; then
			if [ $v_flag -eq 1 ] ; then
				echo "Sourcing pre-backup files from folder $HOME/.bu:"
			fi
			for i in $BU_CONTENTS
			do
				if [ "$i" = "*" ] ; then
					if [ $v_flag -eq 1 ] ; then
						echo "Done sourcing $HOME/.bu."
					fi
					return
				fi
				if [ "${i:0:4}" = "post" -o "${i}" = "bu_excludes" ] ; then
					continue
				fi
				if [ $v_flag -eq 1 ] ; then
					echo "Sourcing $HOME/.bu/$i"
				fi
				source ./$i
			done
			if [ $v_flag -eq 1 ] ; then
				echo "Done sourcing $HOME/.bu."
			fi
		fi
	# Post-Backup stage: Process any file, that begins with the prefix 'post'.
	elif [ "$1" = "post" ] ; then
		cd $HOME/.bu
		BU_CONTENTS=*
		if [ -n "$BU_CONTENTS" ] ; then
			if [ $v_flag -eq 1 ] ; then
				echo "Sourcing post-backup files from folder $HOME/.bu:"
			fi
			for i in $BU_CONTENTS
			do
				if [ "$i" = "*" ] ; then
					if [ $v_flag -eq 1 ] ; then
						echo "Done sourcing $HOME/.bu."
					fi
					return
				fi
				if [ ! "${i:0:4}" = "post" ] ; then
					continue
				fi
				if [ $v_flag -eq 1 ] ; then
					echo "Sourcing $HOME/.bu/$i"
				fi
				source ./$i
			done
			if [ $v_flag -eq 1 ] ; then
				echo "Done sourcing $HOME/.bu."
			fi
		fi
	fi
}

#
# Clean the users backup directory.
#
function make_clean()
{
	local choice

	# Confirm
	echo "This will delete the entire contents of you backupfolder: ${BACKUP_BASE}"
	echo -n "Are you sure? [yes or no]: "
	read -t 30 choice		# We got 30 seconds to make a choice

	case $choice in

	[Yy][Ee][Ss] | [Yy] )			# remove old files : >
		rm -fvr ${BACKUP_BASE}/*
		;;
	[Nn][Oo] | [Nn] )
		echo 'Aborted!'
		exit 1
		;;
	*)
		echo "Terminating."
		exit 1
		;;
	esac
}

#
# Strip a trailing slash from given pathnames, i.e.
# /path/to/file/ becomes /path/to/file
#
function strip_trailing_slash()
{
	local _length _strlen _last_char

	_length=$(awk -v value=$1 'BEGIN {
		n = length(value);
		print n;
	}')

	_last_char=${1:$_strlen-1:1}

	if [ "$_last_char" = "/" ] ; then
		echo ${1:0:$_strlen-1}
	else
		echo $1
	fi
}

#
# Copy the backup to another local device.
#
function copy_backup_local()
{
	local _i

	_i=$1

	if [ -d $_i ] ; then
		if [ -w $_i ] ; then
			_i=$(strip_trailing_slash $_i)
			if [ $v_flag -eq 1 ] ; then
				echo -n "Copying backup to: $_i..."
			fi
			cp -i ${BACKUP_DIR}/${FILENAME} $_i
			if [ $v_flag -eq 1 ] ; then
				echo " done."
			fi
		else
			echo "$_i is not writable."
		fi
	else
		echo "$_i is not a directory."
	fi
}

#
# Get the destination for scp. Passed via -s.
#
function scp_get_destination_path()
{
	local _dest

	_dest=$(awk -v value="$1" 'BEGIN {
		n=split(value, a);
		print a[n];
	}')

	echo $_dest
}

#
# Get the arguments (if any) for scp. Passed via -s.
#
function scp_get_arguments()
{
	local _args

	_args=$(awk -v value="$1" 'BEGIN {
		args=""
		n=split(value, a);
		for(i = 1; i < n; i++)
			args=args" "a[i];

		print args;
	}')

	echo $_args
}

#
# Copy the backup via scp
#
function copy_backup_scp()
{
	local _scp_args _scp_path _file

	_file=$2

	if [ $v_flag -eq 1 ] ; then
		echo "Copying backup via scp:"
	fi

	_scp_args=$(scp_get_arguments "$1")
	_scp_path=$(scp_get_destination_path "$1")

	if [ ! -z "$_scp_args" ] ; then
		scp $_scp_args $_file $_scp_path
	else
		scp $_file $_scp_path
	fi
}

#
# Extract the path to a file: /path/to/file -> /path/to/
#
function get_path_to_file()
{
	local _awkvar _ptf

	# Get the left part of the last "/" aka path to file.
	_ptf=$(awk -v _awkvar=$1 'BEGIN { 
		string = "";
		n = split(_awkvar, a, "/");

		for(i = 2; i < n; i++) 
			string = string"/"a[i];

		string = string"/";
		print string;
		}')

	echo $_ptf
}

#
# Extract the filename out of a full path: /path/to/file -> file
#
function get_filename()
{
	local _awkvar _fn

	# Get the right part of the last "/" aka filename.
	_fn=$(awk -v _awkvar=$1 'BEGIN { 
		n = split(_awkvar, a, "/");
		print a[n];
		}')

	echo $_fn
}

#
# Delete a backup
#
function delete_backup()
{
	local _bu _choice _file _ln _nobs _rf _rf_flag

	# Check for the existence of the backup that we seek to delete.
	_bu=$(realpath $1)
	if [ ! -f $_bu ]; then
		echo "The backup: $_bu doesn't exist."
		exit
	fi

	# Construct the report file and see if it exists in the target directory.
	# If not, continue but set _rf_flag to 1
	_rf_flag=0
	_rf=$(get_path_to_file $_bu)${USER_REPORT_FILE}
	if [ ! -f $_rf ]; then
		_rf=$(get_path_to_file $_bu)${ETC_REPORT_FILE}
		if [ ! -f $_rf ]; then
			echo "The reportfile doesn't exist."
			_rf_flag=1
		fi
	fi

	# Check if the entry in our reportfile is unique. If so, get the line number.
	if [ $_rf_flag -eq 0 ] ; then
		_file=$(get_filename $_bu)
		_nobs=$(cat $_rf | grep -c $_file)
		if [ $_nobs -eq 0 ]; then
			echo "No entry for: $_file in reportfile."
			_rf_flag=1
		elif [ $_nobs -eq 1 ]; then
			_ln=$(cat $_rf | grep -n $_file)
		else
			# Not probable but still...
			echo "Entry for: $_file in reportfile not unique."
			_rf_flag=1
		fi
	fi

	# Make sure and delete the backup. Otherwise abort.
	echo "This will delete your backup: $_bu"
	echo "Are you sure? [yes or no]: "
	read -t 30 _choice		# We got 30 seconds to make a choice

	case $_choice in
	[Yy][Ee][Ss] | [Yy] )
		if [ $_rf_flag -eq 0 ] ; then
			sed -i ${_ln%:*}d $_rf
		fi
		rm $_bu
		exit 0
		;;
	[Nn][Oo] | [Nn] )
		echo 'Aborted!'
		exit 1
		;;
	*)
		echo "Terminating."
		exit 1
		;;
	esac
}

#
# usage function
#
function usage()
{
	echo "usage:"
	echo "backupuser [-hijkpruvx] [-d backup] [-s remote_server] [local_disks]"
	echo "backupuser [-rv] [-s remote_server] etc [local_disks]"
	echo "backupuser clean"
}

############################################################
##### Point of entry

while getopts ":d:hijkprs:uvx" opt ; do
        case $opt in
                d)
                        delete_backup $OPTARG
                        ;;
                h)
                        h_flag=1		# create archive in ${BACKUP_DIR}
                        ;;
                i)
                        j_flag=1		# no pre-backup processing in do_hook
						k_flag=1		# no post-backup processing in do_hook
                        ;;
                j)
                        j_flag=1		# no pre-backup processing in do_hook
                        ;;
                k)
                        k_flag=1		# no post-backup processing in do_hook
                        ;;
                p)
                        p_flag=1		# create installed packages list flag
                        ;;
                r)
                        r_flag=1		# do not log the backup in report_file
                        ;;
                s)
                        s_flag=1		# scp flag
						SCP_DEST=$OPTARG
                        ;;
                u)
                        usage
						exit 0
                        ;;
                v)
                        v_flag=1		# be more verbose
                        ;;
                x)
						x_flag=1		# do not process ~/.bu/bu_excludes
                        ;;
                \?)
                        echo "unkown flag: -$OPTARG."
                        usage
                        exit 1
                        ;;
				:)
						echo "The -$OPTARG flag needs an argument"
                        usage
						exit 1
						;;
        esac
done

shift $((OPTIND-1))

if [ "$1" = "clean" ] ; then
	make_clean
	echo 'Done!'
	exit 0
elif [ "$1" = "etc" ] ; then
	backup_etc
	if [ $s_flag -eq 1 ] ; then
		copy_backup_scp "$SCP_DEST" ${BACKUP_DIR_ETC}/${ETC_NAME}
	fi
	shift
else
	create_backup
	if [ $s_flag -eq 1 ] ; then
		copy_backup_scp "$SCP_DEST" ${BACKUP_DIR}/${FILENAME}
	fi
fi

for i in $*
do
	copy_backup_local $i
done

echo 'Done!'
exit 0

The following script is meant to be run in the folder, to which the backup file has been copied to. It will generate the same reportfile, the backupuser utility creates when performing the backup. If the backups in the destination folder are not corrupted, then the contents of the reportfile will be equivalent to the one's in the user's home folder. To verify, the diff utility can be used.


Here is the source code for bu_check_files:


bu_check_files


#!/bin/sh

# Copyright (c) 2015-2020 Oliver Mahmoudi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions 
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

# Place this script in the folder to which you copy your backups and run it
# to:
# 	a) check the files for errors via gzip --test and 
# 	b) generate their checksums.
# 
# Later, the generated report file can be diffed with the original report file 
# on the users hard drive.

# Variables:
WHO=$(whoami)
USER_REPORT_FILE=${WHO}_report
ETC_REPORT_FILE=etc_report

# Functions:
# create_report_file
# get_path_to_file
# get_filename
# delete_backup

create_report_file()
{
	local _contents
	
	if [ "$1" = "etc" ] ; then
		_contents=etc*.tar.gz
		: > ${ETC_REPORT_FILE}
	else
		_contents=*.tar.gz
		: > ${USER_REPORT_FILE}
	fi

	for i in ${_contents} ; do
		gzip --test ${i} > /dev/null 2>&1
		if [ $? -eq 0 ] ; then
			if [ "$1" = "etc" ] ; then
				sha256sum ${i} >> ${ETC_REPORT_FILE}
			else
				sha256sum ${i} >> ${USER_REPORT_FILE}
			fi
		else
			if [ "$1" = "etc" ] ; then
				echo "File: ${i} is corrupted." >> ${ETC_REPORT_FILE}
			else
				echo "File: ${i} is corrupted." >> ${USER_REPORT_FILE}
			fi
		fi
	done
}

get_path_to_file()
{
	local _awkvar _ptf

	# Get the left part of the last "/" aka path to file.
	_ptf=$(awk -v _awkvar=$1 'BEGIN { 
		string = "";
		n = split(_awkvar, a, "/");

		for(i = 2; i < n; i++) 
			string = string"/"a[i];

		string = string"/";
		print string;
		}')

	echo $_ptf
}

get_filename()
{
	local _awkvar _fn

	# Get the right part of the last "/" aka filename.
	_fn=$(awk -v _awkvar=$1 'BEGIN { 
		n = split(_awkvar, a, "/");
		print a[n];
		}')

	echo $_fn
}

delete_backup()
{
	local _bu _choice _file _ln _nobs _rf _rf_flag

	# Check for the existence of the backup that we seek to delete.
	_bu=$(realpath $1)
	if [ ! -f $_bu ]; then
		echo "The backup: $_bu doesn't exist."
		exit
	fi

	# Construct the report file and see if it exists in the target directory.
	# If not, continue but set _rf_flag to 1
	_rf_flag=0
	_rf=$(get_path_to_file $_bu)${USER_REPORT_FILE}
	if [ ! -f $_rf ]; then
		_rf=$(get_path_to_file $_bu)${ETC_REPORT_FILE}
		if [ ! -f $_rf ]; then
			echo "The reportfile doesn't exist."
			_rf_flag=1
		fi
	fi

	# Check if the entry in our reportfile is unique. If so, get the line number.
	if [ $_rf_flag -eq 0 ] ; then
		_file=$(get_filename $_bu)
		_nobs=$(cat $_rf | grep -c $_file)
		if [ $_nobs -eq 0 ]; then
			echo "No entry for: $_file in reportfile."
			_rf_flag=1
		elif [ $_nobs -eq 1 ]; then
			_ln=$(cat $_rf | grep -n $_file)
		else
			# Not probable but still...
			echo "Entry for: $_file in reportfile not unique."
			_rf_flag=1
		fi
	fi

	# Make sure and delete the backup. Otherwise abort.
	echo "This will delete your backup: $_bu"
	echo "Are you sure? [yes or no]: "
	read -t 30 _choice		# We got 30 seconds to make a choice

	case $_choice in
	[Yy][Ee][Ss] | [Yy] )
		if [ $_rf_flag -eq 0 ] ; then
			sed -i ${_ln%:*}d $_rf
		fi
		rm $_bu
		exit 0
		;;
	[Nn][Oo] | [Nn] )
		echo 'Aborted!'
		exit 1
		;;
	*)
		echo "Terminating."
		exit 1
		;;
	esac
}

usage()
{
	echo "usage:" 
	echo "bu_check_files [-u] [-d backup]"
	echo "bu_check_files etc"
}

### entry point

while getopts ":d:u" opt ; do
        case $opt in
                d)
                        delete_backup $OPTARG
                        ;;
                u)
                        usage
						exit 0
                        ;;
                \?)
                        echo "unkown flag: -$OPTARG."
                        usage
                        exit 1
                        ;;
				:)
						echo "The -$OPTARG flag needs an argument"
                        usage
						exit 1
						;;
        esac
done

shift $((OPTIND-1))

if [ "$1" = "etc" ] ; then
	create_report_file "etc"
else
	create_report_file
fi

echo 'Done!'
exit 0

A gzipped tarball with the source code can be found here:


Version 1.1.1:
File: backupuser-1.1.1.tar.gz
sha256sum: afd15f9f2e0bac3b26394c0136be309cf41adf624de85f31b5d3e49127f82aaf



Alternatively you could clone the source code from github with:



git clone https://github.com/olimah/backupuser