bash3boilerplate/main.sh

249 lines
9.1 KiB
Bash
Raw Normal View History

2014-07-23 09:27:53 +00:00
#!/usr/bin/env bash
2014-07-23 09:28:23 +00:00
# This file:
2016-02-17 12:46:05 +00:00
#
2016-06-21 11:48:08 +00:00
# - Demos BASH3 Boilerplate (change this for your script)
2013-02-25 22:39:51 +00:00
#
# Usage:
2016-02-17 12:46:05 +00:00
#
2016-06-21 11:48:08 +00:00
# LOG_LEVEL=7 ./main.sh -f /tmp/x -d (change this for your script)
2013-02-25 22:39:51 +00:00
#
2016-06-21 11:48:08 +00:00
# Based on a template by BASH3 Boilerplate v2.0.0
# Copyright (c) 2013 Kevin van Zonneveld and contributors
# http://bash3boilerplate.sh/#authors
2013-02-25 22:39:51 +00:00
2016-06-21 11:48:37 +00:00
# Exit on error. Append || true if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
2016-06-21 11:48:37 +00:00
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
2016-06-21 11:48:37 +00:00
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
2016-06-21 11:48:37 +00:00
# Turn on traces, useful while debugging but commented out by default
# set -o xtrace
# Set magic variables for current file, directory, os, etc.
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
2014-11-11 11:41:18 +00:00
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
2016-02-16 20:58:25 +00:00
__base="$(basename ${__file} .sh)"
2016-06-23 14:20:45 +00:00
# Define the environment variables (and their defaults) that this script depends on
LOG_LEVEL="${LOG_LEVEL:-6}" # 7 = debug -> 0 = emergency
NO_COLOR="${NO_COLOR:-}" # true = disable color. otherwise autodetected
2013-02-25 22:39:51 +00:00
### Functions
##############################################################################
2013-02-25 22:39:51 +00:00
function _fmt () {
local color_debug="\x1b[35m"
local color_info="\x1b[32m"
local color_notice="\x1b[34m"
local color_warning="\x1b[33m"
local color_error="\x1b[31m"
local color_critical="\x1b[1;31m"
local color_alert="\x1b[1;33;41m"
local color_emergency="\x1b[1;4;5;33;41m"
local colorvar=color_$1
2013-02-25 22:39:51 +00:00
local color="${!colorvar:-$color_error}"
2014-07-23 09:30:09 +00:00
local color_reset="\x1b[0m"
if [ "${NO_COLOR}" = "true" ] || [[ "${TERM:-}" != "xterm"* ]] || [ -t 1 ]; then
2013-02-25 22:39:51 +00:00
# Don't use colors on pipes or non-recognized terminals
2014-07-23 09:30:53 +00:00
color=""; color_reset=""
2013-02-25 22:39:51 +00:00
fi
echo -e "$(date -u +"%Y-%m-%d %H:%M:%S UTC") ${color}$(printf "[%9s]" ${1})${color_reset}";
}
2014-07-23 09:31:17 +00:00
function emergency () { echo "$(_fmt emergency) ${@}" 1>&2 || true; exit 1; }
function alert () { [ "${LOG_LEVEL}" -ge 1 ] && echo "$(_fmt alert) ${@}" 1>&2 || true; }
function critical () { [ "${LOG_LEVEL}" -ge 2 ] && echo "$(_fmt critical) ${@}" 1>&2 || true; }
function error () { [ "${LOG_LEVEL}" -ge 3 ] && echo "$(_fmt error) ${@}" 1>&2 || true; }
function warning () { [ "${LOG_LEVEL}" -ge 4 ] && echo "$(_fmt warning) ${@}" 1>&2 || true; }
function notice () { [ "${LOG_LEVEL}" -ge 5 ] && echo "$(_fmt notice) ${@}" 1>&2 || true; }
function info () { [ "${LOG_LEVEL}" -ge 6 ] && echo "$(_fmt info) ${@}" 1>&2 || true; }
function debug () { [ "${LOG_LEVEL}" -ge 7 ] && echo "$(_fmt debug) ${@}" 1>&2 || true; }
2013-02-25 22:39:51 +00:00
function help () {
echo "" 1>&2
echo " ${@}" 1>&2
echo "" 1>&2
echo " ${__usage:-No usage available}" 1>&2
echo "" 1>&2
echo " ${__helptext:-}" 1>&2
echo "" 1>&2
2013-02-25 22:39:51 +00:00
exit 1
}
function cleanup_before_exit () {
info "Cleaning up. Done"
}
trap cleanup_before_exit EXIT
### Parse commandline options
##############################################################################
# Commandline options. This defines the usage page, and is used to parse cli
# opts & defaults from. The parsing is unforgiving so be precise in your syntax
# - A short option must be preset for every long option; but every short option
# need not have a long option
# - `--` is respected as the separator between options and arguments
# - We do not bash-expand defaults, so setting '~/app' as a default will not resolve to ${HOME}.
# you can use bash variables to work around this (so use ${HOME} instead)
read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered
-f --file [arg] Filename to process. Required.
-t --temp [arg] Location of tempfile. Default="/tmp/bar"
-v Enable verbose mode, print script as it is executed
-d --debug Enables debug mode
-h --help This page
-n --no-color Disable color output
-1 --one Do just one thing
EOF
read -r -d '' __helptext <<-'EOF' || true # exits non-zero when EOF encountered
This is Bash3 Boilerplate's help text. Feel free to add any description of your
program or elaborate more on command-line arguments. This section is not
parsed and will be added as-is to the help.
EOF
2013-02-25 22:39:51 +00:00
# Translate usage string -> getopts arguments, and set $arg_<flag> defaults
while read line; do
# fetch single character version of option string
opt="$(echo "${line}" |awk '{print $1}' |sed -e 's#^-##')"
# fetch long version if present
long_opt="$(echo "${line}" |awk '/\-\-/ {print $2}' |sed -e 's#^--##')"
long_opt_mangled="$(sed 's#-#_#g' <<< $long_opt)"
# map long name back to short name
varname="short_opt_${long_opt_mangled}"
eval "${varname}=\"${opt}\""
# check if option takes an argument
varname="has_arg_${opt}"
2013-02-25 22:39:51 +00:00
if ! echo "${line}" |egrep '\[.*\]' >/dev/null 2>&1; then
init="0" # it's a flag. init with 0
eval "${varname}=0"
2013-02-25 22:39:51 +00:00
else
opt="${opt}:" # add : if opt has arg
init="" # it has an arg. init with ""
eval "${varname}=1"
2013-02-25 22:39:51 +00:00
fi
opts="${opts:-}${opt}"
2013-02-25 22:39:51 +00:00
varname="arg_${opt:0:1}"
if ! echo "${line}" |egrep '\. Default=' >/dev/null 2>&1; then
eval "${varname}=\"${init}\""
else
match="$(echo "${line}" |sed 's#^.*Default=\(\)#\1#g')"
eval "${varname}=\"${match}\""
fi
done <<< "${__usage}"
2013-02-25 22:39:51 +00:00
# Allow long options like --this
opts="${opts}-:"
2013-02-25 22:39:51 +00:00
# Reset in case getopts has been used previously in the shell.
OPTIND=1
# start parsing command line
set +o nounset # unexpected arguments will cause unbound variables
# to be dereferenced
2013-02-25 22:39:51 +00:00
# Overwrite $arg_<flag> defaults with the actual CLI options
while getopts "${opts}" opt; do
[ "${opt}" = "?" ] && help "Invalid use of script: ${@} "
2013-02-25 22:39:51 +00:00
2016-02-16 21:09:28 +00:00
if [ "${opt}" = "-" ]; then
# OPTARG is long-option-name or long-option=value
if [[ "${OPTARG}" =~ .*=.* ]]; then
# --key=value format
long=${OPTARG/=*/}
long_mangled="$(sed 's#-#_#g' <<< $long)"
2016-02-16 21:09:28 +00:00
# Set opt to the short option corresponding to the long option
eval "opt=\"\${short_opt_${long_mangled}}\""
OPTARG=${OPTARG#*=}
2016-02-16 21:09:28 +00:00
else
# --key value format
# Map long name to short version of option
long_mangled="$(sed 's#-#_#g' <<< $OPTARG)"
eval "opt=\"\${short_opt_${long_mangled}}\""
2016-02-16 21:09:28 +00:00
# Only assign OPTARG if option takes an argument
eval "OPTARG=\"\${@:OPTIND:\${has_arg_${opt}}}\""
# shift over the argument if argument is expected
((OPTIND+=has_arg_${opt}))
fi
# we have set opt/OPTARG to the short value and the argument as OPTARG if it exists
fi
varname="arg_${opt:0:1}"
default="${!varname}"
2013-02-25 22:39:51 +00:00
value="${OPTARG}"
if [ -z "${OPTARG}" ] && [ "${default}" = "0" ]; then
value="1"
2013-02-25 22:39:51 +00:00
fi
eval "${varname}=\"${value}\""
debug "cli arg ${varname} = ($default) -> ${!varname}"
2013-02-25 22:39:51 +00:00
done
set -o nounset # no more unbound variable references expected
2013-02-25 22:39:51 +00:00
shift $((OPTIND-1))
[ "${1:-}" = "--" ] && shift
2013-02-25 22:39:51 +00:00
### Command-line argument switches (like -d for debugmode, -h for showing helppage)
##############################################################################
2013-02-25 22:39:51 +00:00
# debug mode
if [ "${arg_d}" = "1" ]; then
2014-07-23 09:31:34 +00:00
set -o xtrace
2013-02-25 22:39:51 +00:00
LOG_LEVEL="7"
fi
# verbose mode
if [ "${arg_v}" = "1" ]; then
set -o verbose
fi
# no color mode
if [ "${arg_n}" = "1" ]; then
NO_COLOR="true"
fi
2013-02-25 22:39:51 +00:00
# help mode
if [ "${arg_h}" = "1" ]; then
2013-02-26 12:12:53 +00:00
# Help exists with code 1
2013-02-25 22:39:51 +00:00
help "Help using ${0}"
fi
### Validation. Error out if the things required for your script are not present
##############################################################################
2013-02-25 22:39:51 +00:00
[ -z "${arg_f:-}" ] && help "Setting a filename with -f or --file is required"
[ -z "${LOG_LEVEL:-}" ] && emergency "Cannot continue without LOG_LEVEL. "
2013-02-25 22:39:51 +00:00
### Runtime
##############################################################################
2013-02-25 22:39:51 +00:00
2016-02-16 21:19:08 +00:00
info "__file: ${__file}"
info "__dir: ${__dir}"
info "__base: ${__base}"
2016-06-24 11:12:41 +00:00
info "OSTYPE: ${OSTYPE}"
2014-07-29 15:32:23 +00:00
2016-02-16 21:05:11 +00:00
info "arg_f: ${arg_f}"
info "arg_d: ${arg_d}"
info "arg_v: ${arg_v}"
info "arg_h: ${arg_h}"
2016-02-16 21:19:08 +00:00
# All of these go to STDERR, so you can use STDOUT for piping machine readable information to other software
2013-02-25 22:39:51 +00:00
debug "Info useful to developers for debugging the application, not useful during operations."
info "Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required."
notice "Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required."
warning "Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time. This is a debug message"
error "Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time."
critical "Should be corrected immediately, but indicates failure in a primary system, an example is a loss of a backup ISP connection."
alert "Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection."
emergency "A \"panic\" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call."