Advertisement
justin_hanekom

tar-home.sh

Mar 30th, 2019
241
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 9.32 KB | None | 0 0
  1. #!/bin/bash -
  2.  
  3. # File: tar-home.sh
  4. # Copyright (c) 2018-2019 Justin Hanekom <justin_hanekom@yahoo.com>
  5. # Licensed under the MIT License
  6.  
  7. # Permission is hereby granted, free of charge, to any person obtaining
  8. # a copy of this software and associated documentation files
  9. # (the "Software"), to deal in the Software without restriction,
  10. # including without limitation the rights to use, copy, modify, merge,
  11. # publish, distribute, sublicense, and/or sell copies of the Software,
  12. # and to permit persons to whom the Software is furnished to do so,
  13. # subject to the following conditions:
  14. #
  15. # The above copyright notice and this permission notice shall be
  16. # included in all copies or substantial portions of the Software.
  17. #
  18. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  21. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  22. # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  23. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  24. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25.  
  26. # Setup a safe Bash scripting environment
  27.  
  28. set -o errexit      # Exit immediately if an error occurs
  29. set -o noclobber    # Do not allow files to be overwritten via redirect
  30. set -o nounset      # Do not allow unset variables
  31.  
  32. # Set the exit code of a pipeline to the rightmost non-zero on error
  33. set -o pipefail
  34. # Set the internal field separator to newline or tab, but not space
  35. IFS=$'\n\t'
  36.  
  37. # Setup a secure Bash scripting environment by: setting a secure path;
  38. # clearing all aliases; clearing the command path hash; setting the hard limit
  39. # to 0 to turn off core dumps; and setting a secure umask
  40.  
  41. PATH=$(PATH='/bin:/usr/bin' getconf PATH); export PATH
  42. builtin unalias -a
  43. hash -r
  44. ulimit -H -c 0 --
  45.  
  46. UMASK=002
  47. umask ${UMASK}
  48.  
  49. # Global constant definitions
  50.  
  51. readonly OPTIONS='u:s:d:p:x:k:rvh'
  52. readonly LONGOPTS='user:,srcdir:,destdir:,prefix:,suffix:,keep:,remove,verbose,help'
  53. readonly TIMESTAMP=$(date '+%Y%m%d%H%M%S')
  54. readonly STARTED_AT=$(date '+%s')
  55.  
  56. # Global variable declarations
  57.  
  58. USER_NAME=''
  59. SRC_DIR=''
  60. DEST_DIR=''
  61. PREFIX="home_"
  62. SUFFIX='.tar.gz'
  63. KEEP=5
  64. IS_REMOVE=''
  65. IS_VERBOSE=''
  66.  
  67. ################################################################################
  68. # Function:     chomp_slash
  69. # Description:  Removes any trailing slash ("/") from a string
  70. # Arguments:    $1 :- String from which to remove any trailing slashes
  71. # Ouputs:       Prints string with trailing slashes removed
  72. function chomp_slash {
  73.     local dir="$1"
  74.     while [ "${dir:(-1)}" = '/' ]; do
  75.         dir=${dir::-1}
  76.     done
  77.     echo "${dir}"
  78. }
  79.  
  80. ################################################################################
  81. # Function:     echo_if_verbose
  82. # Description:  Prints $@ if IS_VERBOSE is not empty
  83. # Arguments:    $@ :- Content to echo if ${IS_VERBOSE} is not empty
  84. # Requires:     ${IS_VERBOSE} set to a non-empty string if verbose is on
  85. # Outputs:      Prints $@ if ${IS_VERBOSE} is not empty
  86. function echo_if_verbose {
  87.     if [ -n "${IS_VERBOSE}" ]; then
  88.         echo "$@"
  89.     fi
  90. }
  91.  
  92. ################################################################################
  93. # Function:     trim
  94. # Description:  Trims any/all whitespace from the beginning and end of a string
  95. # Arguments:    $1 :- The string from which to trim whitespace
  96. # Outputs:      Prints string with any leading/trailing whitespace removed
  97. function trim {
  98.     local str="$1"
  99.     str="${str#"${str%%[![:space:]]*}"}"
  100.    str="${str%"${str##*[![:space:]]}"}"
  101.     echo "$1"
  102. }
  103.  
  104. ################################################################################
  105. # Function:     usage_exit
  106. # Description:  Prints the usage message for this script and then exits
  107. # Arguments:    $1 :- Exit code; defaults to 1
  108. # Outputs:      Prints description of how to call this script
  109. function usage_exit {
  110.     local exit_code=1
  111.  
  112.     if [[ ${1-} =~ ^[0-9]+$ ]]; then
  113.         exit_code="$1"
  114.     fi
  115.  
  116.     cat << EOT
  117. Usage: $(basename "$0") [options]...
  118.     -u|--user       <val>   (Required) The owner of the generated archive file
  119.     -s|--srcdir     <val>   (Required) The owners home directory
  120.     -d|--destdir    <val>   (Required) The dir into which to save the new file
  121.     -p|--prefix     <val>   The prefix of the generated archive file ('home_')
  122.     -x|--suffix     <val>   The suffix of the generated archive file ('.tar.gz')
  123.     -k|--keep       <val>   How many archives to keep (5)
  124.     -r|--remove             Causes previous archive files to be removed
  125.     -v|--verbose            Displays verbose output
  126.     -h|--help               Displays this message and aborts the script
  127.  
  128. NOTE: The <user>, <srcdir>, and <destdir> arguments are mandatory
  129. and *must* be supplied
  130.  
  131. EOT
  132.     exit "${exit_code}"
  133. }
  134.  
  135. ################################################################################
  136. # Function:     parse_cmdline
  137. # Description:  Parses the command-line using the enhanced version of getopt
  138. # Arguments:    $@ :- Command-line arguments (required)
  139. # Requires:     Global variables $USER_NAME, $SRC_DIR, $DEST_DIR, $PREFIX,
  140. #               $SUFFIX, $KEEP, $IS_REMOVE, and $IS_VERBOSE
  141. # Outputs:      Sets above-mentioned global variables based on given
  142. #               command-line arguments
  143. function parse_cmd_line {
  144.     # Ensure that the enhanced version of getopt is available
  145.  
  146.     ! getopt --test > /dev/null
  147.     if (( "${PIPESTATUS[0]}" != 4 )); then
  148.         echo "I'm sorry, the enhanced version of getopt is required. Exiting." >&2
  149.         exit 1
  150.     fi
  151.  
  152.     # Initialize the global variables
  153.  
  154.     USER_NAME=''
  155.     SRC_DIR=''
  156.     DEST_DIR=''
  157.     PREFIX="home_"
  158.     SUFFIX='.tar.gz'
  159.     KEEP=5
  160.     IS_REMOVE=''
  161.     IS_VERBOSE=''
  162.  
  163.     # Parse the command-line options
  164.  
  165.      if ! readonly PARSED_OPTIONS=$(getopt --options=${OPTIONS} \
  166.                                     --longoptions=${LONGOPTS} \
  167.                                     --name "$(basename "$0")" -- "$@"); then
  168.         echo 'Unknown error parsing getopt options. Exiting.' >&2
  169.         exit 2
  170.     fi
  171.  
  172.     eval set -- "${PARSED_OPTIONS}"
  173.  
  174.     # Extract command-line options and their arguments, if any
  175.  
  176.     while (( $# >= 1 )); do
  177.         case "$1" in
  178.             -u|--user)
  179.                 USER_NAME=$(trim "$2") ; shift 2
  180.                 ;;
  181.             -s|--srcdir)
  182.                 SRC_DIR=$(chomp_slash "$(trim "$2")") ; shift 2
  183.                 ;;
  184.             -d|--destdir)
  185.                 DEST_DIR=$(chomp_slash "$(trim "$2")") ; shift 2
  186.                 ;;
  187.             -p|--prefix)
  188.                 PREFIX=$(trim "$2") ; shift 2
  189.                 ;;
  190.             -x|--suffix)
  191.                 SUFFIX=$(trim "$2") ; shift 2
  192.                 ;;
  193.             -k|--keep)
  194.                 KEEP=$(trim "$2") ; shift 2
  195.                 ;;
  196.             -r|--remove)
  197.                 IS_REMOVE='true' ; shift
  198.                 ;;
  199.             -v|--verbose)
  200.                 IS_VERBOSE='true' ; shift
  201.                 ;;
  202.             -h|--help)
  203.                 usage_exit 0
  204.                 ;;
  205.             --)
  206.                 shift ; break
  207.                 ;;
  208.             *)
  209.                 usage_exit 3 >&2
  210.                 ;;
  211.         esac
  212.     done
  213.  
  214.     # Ensure that required options have been supplied
  215.  
  216.     if [[ -z "${USER_NAME}" || -z "${SRC_DIR}" || -z "${DEST_DIR}" ]]; then
  217.         usage_exit 4 >&2
  218.     fi
  219. } # parse_cmd_line
  220.  
  221. ################################################################################
  222. # Start of program
  223.  
  224. parse_cmd_line "$@"
  225.  
  226. # Determine the tar options
  227.  
  228. readonly DEST_FILENAME="${DEST_DIR}/${PREFIX}${TIMESTAMP}${SUFFIX}"
  229.  
  230. if [ -n "$IS_VERBOSE" ]; then
  231.     readonly VERBOSE='--verbose'
  232. else
  233.     readonly VERBOSE=''
  234. fi
  235.  
  236. readonly FLAGS="--create --preserve-permissions --atime-preserve \
  237.    ${VERBOSE} --use-compress-program='pigz'"
  238.  
  239. TAR_CMD="sudo tar ${FLAGS} --file='${DEST_FILENAME}' '$(basename "${SRC_DIR}")'"
  240. [ -z "${IS_VERBOSE}" ] && TAR_CMD="${TAR_CMD} &>/dev/null"
  241.  
  242. # Optionally keep the last ${KEEP}-1 archives
  243.  
  244. if [ -n "${IS_REMOVE}" ]; then
  245.     files_signature="${DEST_DIR}/${PREFIX}*${SUFFIX}"
  246.  
  247.     if (( "${KEEP}" > 0 )); then
  248.         set +e  # Temporarily disable error exiting
  249.         sudo ls -1tr "${files_signature}" | \
  250.             tail +"${KEEP}" | sudo xargs rm --force "${VERBOSE}"
  251.         set -e  # Reenable error exiting
  252.     else
  253.         sudo rm --force "${VERBOSE}" "${files_signature}"
  254.     fi
  255. fi
  256.  
  257. exit
  258.  
  259. # Archive the source directory
  260. #
  261. # Using cd instead of tar's -C (i.e., directory) flag because I was getting
  262. # the unexpected result of only the Desktop directory being archived
  263. # if this script is executed by root's crontab;
  264. # archiving from the $SRC_DIRs parent so that the archive is rooted at the
  265. # $SRC_DIRs basename
  266.  
  267. pushd . &>/dev/null
  268. cd "$(dirname "${SRC_DIR}")"
  269. eval "${TAR_CMD}" || true
  270. popd &>/dev/null
  271.  
  272. # Change the owner of the generated archive file
  273.  
  274. sudo chown "${USER_NAME}":"${USER_NAME}" "${DEST_FILENAME}" 2>/dev/null
  275. echo_if_verbose "Changed owner of: ${DEST_FILENAME} to: ${USER_NAME}"
  276.  
  277. # Report that we are done, and how long the script took
  278.  
  279. if [ -n "${IS_VERBOSE}" ]; then
  280.     readonly ENDED_AT=$(date '+%s')
  281.     readonly DIFF=$(( ENDED_AT - STARTED_AT ))
  282.     echo "Done, in ${DIFF} seconds"!
  283. fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement