neechbear/blip

View on GitHub
examples/boilerplate.sh

Summary

Maintainability
Test Coverage
#!/usr/bin/env bash
# vim:ts=2:sw=2:tw=79

set -Eeuo pipefail
shopt -s extglob
shopt -s nocasematch
shopt -s extdebug

# Secure environment.
IFS=$' \t\n'
unset -f unalias
# shellcheck disable=SC1001
\unalias -a
unset -f command
if ! PATH="$(command -p getconf PATH 2>/dev/null)" && [[ -z "$PATH" ]]; then
  PATH="/usr/bin:/bin"
fi

# Load libraries.
if ! BLIP_REQUIRE_VERSION=0.9.1 PATH="$PATH:/usr/lib:$HOME/bin" source blip.bash
then
  >&2 echo "Missing dependency 'blip' (https://nicolaw.uk/blip); aborting!"
  exit 2
fi

# Report when we exited due to Bash errors.
exec 3>&1 4>&2
trap 'declare rc=$?;
      >&2 echo "Unexpected error executing $BASH_COMMAND at ${BASH_SOURCE[0]} line $LINENO";
      __blip_stacktrace__ >&2; exit $rc' ERR

_parse_command_line_arguments () {
  cmdarg_info "header" "Description of the script."
  cmdarg_info "author" "Nicola Worthington <nicolaw@tfb.net>."
  cmdarg_info "copyright" "(C) 2017 Copyright message."

  cmdarg_info "footer" \
    "For help configuring the command line arguments using blip.bash, see" \
    "https://github.com/neechbear/blip/blob/master/CMDARG.md."

  cmdarg 'm:'   'mandatory' 'Uber important mandatory argument'
  cmdarg 'f:[]' 'foo_list'  'A list of things you need'
  cmdarg 'o?'   'optional'  'Some optional argument'
  cmdarg 'b'    'boolean'   'Some boolean argument'
  cmdarg 'V'    'verbose'   'Display more verbose informational output'

  cmdarg_parse "$@" || return $?
}

main () {
  # Command line argument processing.
  # shellcheck disable=SC2034
  declare -r VERSION="0.0.1"
  declare -A cmdarg_cfg=()
  declare -a foo_list=()
  _parse_command_line_arguments "$@" 1>&3 2>&4 || exit $?

  if [[ -n "${cmdarg_cfg[verbose]}" || -n "${DEBUG:-}" ]] ; then
    for i in "${!cmdarg_cfg[@]}" ; do
      printf "\${cmdarg_cfg[%s]}=%q\n" "$i" "${cmdarg_cfg[$i]}"
    done
    for i in "${!foo_list[@]}" ; do
      printf "\${foo_list[%s]}=%q\n" "$i" "${foo_list[$i]}"
    done
  fi
  [[ -z "${cmdarg_cfg[help]:-}" ]] || return 0

  # Main script.
  declare -i rc=0
  echo stdout
  >&2 echo stderr

  return $rc
}

# Called as a command script (not sourced as a library).
if [[ "$(readlink "${BASH_SOURCE[0]}")" == "$(readlink "$0")" ]] ; then
  if ! is_true "${DEBUG:-}" ; then
    unset DEBUG
  else
    # Turn bash xtrace debugging on if $DEBUG environment is true.
    exec 19> "${TMPDIR:-/tmp}/${BASH_SOURCE[0]##*/}.xtrace.$$.log"
    { # Print debug header to xtrace log file.
      printf "Command line: \"%q\"" "$0" ; printf " \"%q\"" "$@" ; printf "\n"
      printf "\$-: %s\n" "$-"
      printf "BASHOPTS: %s\n" "$BASHOPTS"
      printf "SHELLOPTS: %s\n" "$SHELLOPTS"
      printf "Start time: %(%Y%m%d %H%M%S %z)T\n" -2
      if is_in_path "git" && [[ -d "${BASH_SOURCE[0]%/*}/.git" ]] ; then
        printf "Git revision: %s\n" \
          "$(git -C "${BASH_SOURCE[0]%/*}" rev-parse --verify HEAD 2>&1)"
      fi
      printf "Hostname: %s\n" "${HOSTNAME:-$(hostname -f 2>&1)}"
      printf "\n" ; env ; printf "\n"
    } >&19 2>&19
    export BASH_XTRACEFD=19
    export PS4='+ $$($BASHPID) +${SECONDS}s (${BASH_SOURCE[0]}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
    set -vx
  fi

  # Logging to syslog, and console with process, PID and timestamp prefix.
  if is_in_path "logger" && is_in_path "ts" ; then
    exec > >( 2>&-; logger -s -t "${0##*/}[$$]" -p user.info  2>&1 \
            | ts "%d-%m-%y %X" | tee -ia "${0%.*}.log" ) \
        2> >( logger -s -t "${0##*/}[$$]" -p user.error 2>&1 \
            | ts "%d-%m-%y %X" | tee -ia "${0%.*}.log" >&2 )
    sleep 0.1
  fi

  # Call main application.
  main "$@"
fi