Bash Scripts

Create Incremental Backup with Rsync

#!/bin/bash
# Create incremental backups using rsync with hard links
# Current impementation meant for daily backups

# TODO: Implement arguments for customizability- number of backups, date interval, source and destination folders

current_date=$(date)
to_backup=("/etc" "/home" "/root" "/var" "/srv" "/opt")
# number of backups to keep
backup_backlog=10

# Change to backup folder
cd /backup

# rotate backups
# wildcard allows for missing days due to downtime (using async scheduler)
rm -rf backup.*.${backup_backlog}
for (( log_number=$backup_backlog; log_number >= 2; log_number-- )); do
    log_date=$(ls | grep backup.*.$((${log_number}-1)) | cut -d'.' -f 2)
    # If the destination folder doesn't have the max number of backups (backup_backlog), filenaming gets weird
    # This makes sure that if any of the backup directories are missing, an empty file with the right name is added
    # New empty file keeps naming convention consistent
    if [ ! -d "backup.${log_date}.$(( ${log_number} - 1 ))" ]; then
        touch "backup.$(date --date="$(date) -${log_number} days" '+%F').$(( ${log_number} - 1 ))"
        log_date=$(ls | grep backup.*.$((${log_number}-1)) | cut -d'.' -f 2)
    fi
    mv "backup.${log_date}.$(( ${log_number} - 1 ))" "backup.${log_date}.${log_number}"
done
cp -al "backup.$(ls | grep backup.*.0 | cut -d'.' -f 2).0" "backup.$(ls | grep backup.*.0 | cut -d'.' -f 2).1"

# make new backup
rsync -a --dry-run --delete --exclude="/home/*/.cache" ${to_backup[@]} "backup.$(ls | grep backup.*.0 | cut -d'.' -f 2).0"
# Keeps naming convention consistent
mv "backup.$(ls | grep backup.*.0 | cut -d'.' -f 2).0" "backup.$(date '+%F').0/"

Check if certain files or directories have changed

#!/bin/bash

if ! [ -f /var/opt/org_hashes ]; then
        cur_file="/var/opt/org_hashes"
else
        cur_file="/var/opt/check_hashes"
        rm -f $cur_file
fi

echo "$(sha256sum /etc/passwd)" >>              ${cur_file}
echo "$(sha256sum /etc/shadow)" >>              ${cur_file}
echo "$(sha256sum /etc/ssh/sshd_config)" >>     ${cur_file}
echo "$(sha256sum /etc/crontab)" >>             ${cur_file}

systemctl | grep service >> /tmp/service_list
echo "$(sha256sum /tmp/service_list)" >>        ${cur_file} 
rm -f /tmp/service_list

for file in $(echo $PATH | sed 's/:/ /g'); do
        ls -l $file > /tmp/$(echo $file | sed 's/\//_/g')
        sha256sum /tmp/$(echo $file | sed 's/\//_/g') >> $cur_file
        rm -f /tmp/$(echo $file | sed 's/\//_/g')
done


echo "`sha256sum $(which sshd)`" >>             ${cur_file}
echo "`sha256sum $(which ps)`" >>               ${cur_file}
echo "`sha256sum $(which netstat)`" >>          ${cur_file}
echo "`sha256sum $(which lsof)`" >>             ${cur_file}
echo "`sha256sum $(which ss)`" >>               ${cur_file}

if [ -f /var/opt/check_hashes ]; then
        if [ "$(sha256sum /var/opt/check_hashes | cut -d ' ' -f 1)" = "$(sha256sum /var/opt/org_hashes | cut -d ' ' -f 1)" ]; then
                echo "Files are unchanged"
        else
                echo "Files Changed!"
        fi
fi

Generate an arbitrary number of test files in a specified folder

#!/bin/sh
# Generates an arbitrary number of files in a given directory
# Default content of files is "test", but user can provide a custom string or the content of a file

# Make sure all vars are empty
unset -v target_dir
unset -v count
unset -v content

show_help() {
	echo "
Usage: ./filegen <-d Directory> <-n Count> [-c String | Path]
  -d:\tTarget directory
  -n:\tNumber of files to generate
  -c:\tContent of generated file, can be custom string or content of given file (defaults to "test")

For Example: ./filegen.sh -d test_directory -n 100 -c "Test Content"

  -h:\tDisplays this message
"
}

# get all flags
while getopts ":c:d:n:h" opt; do
	case "${opt}" in
		d) target_dir=$OPTARG;;
		n) count=$OPTARG;;
		c) content=$OPTARG;;
		h) show_help; exit 0;;
		*)
			echo 'Incorrect Usage:' >&2
			show_help
			exit 1
	esac
done

shift "$(( OPTIND - 1 ))"

# Make sure target directory and count were specified
if [ -z "$target_dir" ] || [ -z "$count" ]; then
	echo 'Incorrect usage: Please specify target directory (-d) and file count (-n)'
	show_help
	exit 1
fi

# Check if $content is a file, if so read file contents into variable
if [ -f "$content" ]; then
	if ! [ -w "$content" ]; then
		echo 'Insufficient Privileges, using content "test"'
		content='test'
	else
		echo "Using file content from: '$content'"
		content="$(cat $content)"
	fi
fi

# If $content wasn't a file or file was empty, make sure content variable is populated
if [ -z "$content" ]; then
	content='test'
fi

# check if count is an int
case $count in
	''|*[!0-9]*) echo "$count is not an integer!"; exit 1;;
	*) continue
esac

# If existing directory, make sure there are permissions
if [ -d $target_dir ]; then
	if ! [ -w $target_dir ]; then
		echo "Permission denied!"
		exit 1
	fi
fi

# Make directory
if mkdir -p $target_dir 2>/dev/null; then
	# If the directory is made, make sure there are no pre-existing test files
	cd $target_dir
	rm test_*.txt 2> /dev/null
	continue
else
	# Fails, usually means bad permissions
	echo 'Cannot create directory!'
	exit 1
fi

# POSIX shell compatible for loop
i=1
while [ "$i" -le "$count" ]; do
	echo "$content" > test_${i}.txt
	i=$((i+1))
done
unset -v i

echo "Done creating ${count} files!"
exit 0