2014-07-23 09:27:53 +00:00
#!/usr/bin/env bash
2014-07-23 09:28:23 +00:00
# BASH3 Boilerplate
2013-02-25 22:39:51 +00:00
#
2014-07-23 09:28:23 +00:00
# This file:
# - Is a template to write better bash scripts
# - Is delete-key friendly, in case you don't need e.g. command line option parsing
#
# More info:
# - https://github.com/kvz/bash3boilerplate
# - http://kvz.io/blog/2013/02/26/introducing-bash3boilerplate/
#
2014-07-23 09:31:42 +00:00
# Version 1.0.0
2014-07-23 09:28:23 +00:00
#
# Authors:
# - Kevin van Zonneveld (http://kvz.io)
2013-02-25 22:39:51 +00:00
#
# Usage:
2014-07-23 09:34:05 +00:00
# LOG_LEVEL=7 ./main.sh -f /tmp/x -d
2013-02-25 22:39:51 +00:00
#
# Licensed under MIT
2014-07-23 09:28:23 +00:00
# Copyright (c) 2013 Kevin van Zonneveld (http://kvz.io)
2013-02-25 22:39:51 +00:00
### Configuration
#####################################################################
2014-07-23 09:28:38 +00:00
# Environment variables and their defaults
LOG_LEVEL = " ${ LOG_LEVEL :- 6 } " # 7 = debug -> 0 = emergency
2013-02-25 22:39:51 +00:00
2014-07-23 09:28:54 +00:00
# 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
2013-02-25 22:39:51 +00:00
read -r -d '' usage <<-'EOF'
2013-02-26 12:12:53 +00:00
-f [ arg] Filename to process. Required.
-t [ arg] Location of tempfile. Default = "/tmp/bar"
2013-02-25 22:39:51 +00:00
-d Enables debug mode
-h This page
EOF
# Set magic variables for current FILE & DIR
2014-07-23 09:29:42 +00:00
__dir = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " && pwd ) "
__file = " ${ __dir } / $( basename " ${ 0 } " ) "
2013-02-25 22:39:51 +00:00
### Functions
#####################################################################
function _fmt ( ) {
2014-07-23 09:30:09 +00:00
local color_ok = "\x1b[32m"
local color_bad = "\x1b[31m"
2013-02-25 22:39:51 +00:00
2014-07-23 09:30:09 +00:00
local color = " ${ color_bad } "
2013-02-25 22:39:51 +00:00
if [ " ${ 1 } " = "debug" ] || [ " ${ 1 } " = "info" ] || [ " ${ 1 } " = "notice" ] ; then
color = " ${ color_ok } "
fi
2014-07-23 09:30:09 +00:00
local color_reset = "\x1b[0m"
2014-07-23 09:30:33 +00:00
if [ [ " ${ 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 ( ) {
2014-07-23 09:34:05 +00:00
echo "" 1>& 2
echo " ${ @ } " 1>& 2
echo "" 1>& 2
echo " ${ usage } " 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
#####################################################################
# Translate usage string -> getopts arguments, and set $arg_<flag> defaults
while read line; do
opt = " $( echo " ${ line } " | awk '{print $1}' | sed -e 's#^-##' ) "
if ! echo " ${ line } " | egrep '\[.*\]' >/dev/null 2>& 1; then
init = "0" # it's a flag. init with 0
else
opt = " ${ opt } : " # add : if opt has arg
init = "" # it has an arg. init with ""
fi
opts = " ${ opts } ${ opt } "
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 } "
# Reset in case getopts has been used previously in the shell.
OPTIND = 1
# Overwrite $arg_<flag> defaults with the actual CLI options
while getopts " ${ opts } " opt; do
line = " $( echo " ${ usage } " | grep " \- ${ opt } " ) "
[ " ${ opt } " = "?" ] && help " Invalid use of script: ${ @ } "
varname = " arg_ ${ opt : 0 : 1 } "
default = " ${ !varname } "
value = " ${ OPTARG } "
if [ -z " ${ OPTARG } " ] && [ " ${ default } " = "0" ] ; then
value = "1"
fi
eval " ${ varname } =\" ${ value } \" "
debug " cli arg ${ varname } = ( $default ) -> ${ !varname } "
done
shift $(( OPTIND-1))
[ " $1 " = "--" ] && shift
2013-02-26 12:12:53 +00:00
### Switches (like -d for debugmdoe, -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
# 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
2013-02-26 12:12:53 +00:00
### Validation (decide what's required for running your script and error out)
2013-02-25 22:39:51 +00:00
#####################################################################
2013-02-26 12:12:53 +00:00
[ -z " ${ arg_f } " ] && help "Setting a filename with -f is required"
[ -z " ${ LOG_LEVEL } " ] && emergency "Cannot continue without LOG_LEVEL. "
2013-02-25 22:39:51 +00:00
### Runtime
#####################################################################
# Exit on error. Append ||true if you expect an error.
2013-09-06 11:24:18 +00:00
# set -e is safer than #!/bin/bash -e because that is neutralised if
2013-02-25 22:39:51 +00:00
# someone runs your script like `bash yourscript.sh`
2014-07-23 09:31:34 +00:00
set -o errexit
set -o nounset
2013-02-25 22:39:51 +00:00
# Bash will remember & return the highest exitcode in a chain of pipes.
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`
set -o pipefail
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."