Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash -
- # File: tar-home.sh
- # Copyright (c) 2018-2019 Justin Hanekom <justin_hanekom@yahoo.com>
- # Licensed under the MIT License
- # Permission is hereby granted, free of charge, to any person obtaining
- # a copy of this software and associated documentation files
- # (the "Software"), to deal in the Software without restriction,
- # including without limitation the rights to use, copy, modify, merge,
- # publish, distribute, sublicense, and/or sell copies of the Software,
- # and to permit persons to whom the Software is furnished to do so,
- # subject to the following conditions:
- #
- # The above copyright notice and this permission notice shall be
- # included in all copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- # Setup a safe Bash scripting environment
- set -o errexit # Exit immediately if an error occurs
- set -o noclobber # Do not allow files to be overwritten via redirect
- set -o nounset # Do not allow unset variables
- # Set the exit code of a pipeline to the rightmost non-zero on error
- set -o pipefail
- # Set the internal field separator to newline or tab, but not space
- IFS=$'\n\t'
- # Setup a secure Bash scripting environment by: setting a secure path;
- # clearing all aliases; clearing the command path hash; setting the hard limit
- # to 0 to turn off core dumps; and setting a secure umask
- PATH=$(PATH='/bin:/usr/bin' getconf PATH); export PATH
- builtin unalias -a
- hash -r
- ulimit -H -c 0 --
- UMASK=002
- umask ${UMASK}
- # Global constant definitions
- readonly OPTIONS='u:s:d:p:x:k:rvh'
- readonly LONGOPTS='user:,srcdir:,destdir:,prefix:,suffix:,keep:,remove,verbose,help'
- readonly TIMESTAMP=$(date '+%Y%m%d%H%M%S')
- readonly STARTED_AT=$(date '+%s')
- # Global variable declarations
- USER_NAME=''
- SRC_DIR=''
- DEST_DIR=''
- PREFIX="home_"
- SUFFIX='.tar.gz'
- KEEP=5
- IS_REMOVE=''
- IS_VERBOSE=''
- ################################################################################
- # Function: chomp_slash
- # Description: Removes any trailing slash ("/") from a string
- # Arguments: $1 :- String from which to remove any trailing slashes
- # Ouputs: Prints string with trailing slashes removed
- function chomp_slash {
- local dir="$1"
- while [ "${dir:(-1)}" = '/' ]; do
- dir=${dir::-1}
- done
- echo "${dir}"
- }
- ################################################################################
- # Function: echo_if_verbose
- # Description: Prints $@ if IS_VERBOSE is not empty
- # Arguments: $@ :- Content to echo if ${IS_VERBOSE} is not empty
- # Requires: ${IS_VERBOSE} set to a non-empty string if verbose is on
- # Outputs: Prints $@ if ${IS_VERBOSE} is not empty
- function echo_if_verbose {
- if [ -n "${IS_VERBOSE}" ]; then
- echo "$@"
- fi
- }
- ################################################################################
- # Function: trim
- # Description: Trims any/all whitespace from the beginning and end of a string
- # Arguments: $1 :- The string from which to trim whitespace
- # Outputs: Prints string with any leading/trailing whitespace removed
- function trim {
- local str="$1"
- str="${str#"${str%%[![:space:]]*}"}"
- str="${str%"${str##*[![:space:]]}"}"
- echo "$1"
- }
- ################################################################################
- # Function: usage_exit
- # Description: Prints the usage message for this script and then exits
- # Arguments: $1 :- Exit code; defaults to 1
- # Outputs: Prints description of how to call this script
- function usage_exit {
- local exit_code=1
- if [[ ${1-} =~ ^[0-9]+$ ]]; then
- exit_code="$1"
- fi
- cat << EOT
- Usage: $(basename "$0") [options]...
- -u|--user <val> (Required) The owner of the generated archive file
- -s|--srcdir <val> (Required) The owners home directory
- -d|--destdir <val> (Required) The dir into which to save the new file
- -p|--prefix <val> The prefix of the generated archive file ('home_')
- -x|--suffix <val> The suffix of the generated archive file ('.tar.gz')
- -k|--keep <val> How many archives to keep (5)
- -r|--remove Causes previous archive files to be removed
- -v|--verbose Displays verbose output
- -h|--help Displays this message and aborts the script
- NOTE: The <user>, <srcdir>, and <destdir> arguments are mandatory
- and *must* be supplied
- EOT
- exit "${exit_code}"
- }
- ################################################################################
- # Function: parse_cmdline
- # Description: Parses the command-line using the enhanced version of getopt
- # Arguments: $@ :- Command-line arguments (required)
- # Requires: Global variables $USER_NAME, $SRC_DIR, $DEST_DIR, $PREFIX,
- # $SUFFIX, $KEEP, $IS_REMOVE, and $IS_VERBOSE
- # Outputs: Sets above-mentioned global variables based on given
- # command-line arguments
- function parse_cmd_line {
- # Ensure that the enhanced version of getopt is available
- ! getopt --test > /dev/null
- if (( "${PIPESTATUS[0]}" != 4 )); then
- echo "I'm sorry, the enhanced version of getopt is required. Exiting." >&2
- exit 1
- fi
- # Initialize the global variables
- USER_NAME=''
- SRC_DIR=''
- DEST_DIR=''
- PREFIX="home_"
- SUFFIX='.tar.gz'
- KEEP=5
- IS_REMOVE=''
- IS_VERBOSE=''
- # Parse the command-line options
- if ! readonly PARSED_OPTIONS=$(getopt --options=${OPTIONS} \
- --longoptions=${LONGOPTS} \
- --name "$(basename "$0")" -- "$@"); then
- echo 'Unknown error parsing getopt options. Exiting.' >&2
- exit 2
- fi
- eval set -- "${PARSED_OPTIONS}"
- # Extract command-line options and their arguments, if any
- while (( $# >= 1 )); do
- case "$1" in
- -u|--user)
- USER_NAME=$(trim "$2") ; shift 2
- ;;
- -s|--srcdir)
- SRC_DIR=$(chomp_slash "$(trim "$2")") ; shift 2
- ;;
- -d|--destdir)
- DEST_DIR=$(chomp_slash "$(trim "$2")") ; shift 2
- ;;
- -p|--prefix)
- PREFIX=$(trim "$2") ; shift 2
- ;;
- -x|--suffix)
- SUFFIX=$(trim "$2") ; shift 2
- ;;
- -k|--keep)
- KEEP=$(trim "$2") ; shift 2
- ;;
- -r|--remove)
- IS_REMOVE='true' ; shift
- ;;
- -v|--verbose)
- IS_VERBOSE='true' ; shift
- ;;
- -h|--help)
- usage_exit 0
- ;;
- --)
- shift ; break
- ;;
- *)
- usage_exit 3 >&2
- ;;
- esac
- done
- # Ensure that required options have been supplied
- if [[ -z "${USER_NAME}" || -z "${SRC_DIR}" || -z "${DEST_DIR}" ]]; then
- usage_exit 4 >&2
- fi
- } # parse_cmd_line
- ################################################################################
- # Start of program
- parse_cmd_line "$@"
- # Determine the tar options
- readonly DEST_FILENAME="${DEST_DIR}/${PREFIX}${TIMESTAMP}${SUFFIX}"
- if [ -n "$IS_VERBOSE" ]; then
- readonly VERBOSE='--verbose'
- else
- readonly VERBOSE=''
- fi
- readonly FLAGS="--create --preserve-permissions --atime-preserve \
- ${VERBOSE} --use-compress-program='pigz'"
- TAR_CMD="sudo tar ${FLAGS} --file='${DEST_FILENAME}' '$(basename "${SRC_DIR}")'"
- [ -z "${IS_VERBOSE}" ] && TAR_CMD="${TAR_CMD} &>/dev/null"
- # Optionally keep the last ${KEEP}-1 archives
- if [ -n "${IS_REMOVE}" ]; then
- files_signature="${DEST_DIR}/${PREFIX}*${SUFFIX}"
- if (( "${KEEP}" > 0 )); then
- set +e # Temporarily disable error exiting
- sudo ls -1tr "${files_signature}" | \
- tail +"${KEEP}" | sudo xargs rm --force "${VERBOSE}"
- set -e # Reenable error exiting
- else
- sudo rm --force "${VERBOSE}" "${files_signature}"
- fi
- fi
- exit
- # Archive the source directory
- #
- # Using cd instead of tar's -C (i.e., directory) flag because I was getting
- # the unexpected result of only the Desktop directory being archived
- # if this script is executed by root's crontab;
- # archiving from the $SRC_DIRs parent so that the archive is rooted at the
- # $SRC_DIRs basename
- pushd . &>/dev/null
- cd "$(dirname "${SRC_DIR}")"
- eval "${TAR_CMD}" || true
- popd &>/dev/null
- # Change the owner of the generated archive file
- sudo chown "${USER_NAME}":"${USER_NAME}" "${DEST_FILENAME}" 2>/dev/null
- echo_if_verbose "Changed owner of: ${DEST_FILENAME} to: ${USER_NAME}"
- # Report that we are done, and how long the script took
- if [ -n "${IS_VERBOSE}" ]; then
- readonly ENDED_AT=$(date '+%s')
- readonly DIFF=$(( ENDED_AT - STARTED_AT ))
- echo "Done, in ${DIFF} seconds"!
- fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement