Skip to content

Instantly share code, notes, and snippets.

@disaac
Forked from rkottmann/bash-template
Created April 13, 2021 13:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save disaac/fe963fd3d3c7c4a460b7271b062d0aba to your computer and use it in GitHub Desktop.
Save disaac/fe963fd3d3c7c4a460b7271b062d0aba to your computer and use it in GitHub Desktop.
A template bash script based on google style guide with some little improvements
#!/bin/bash
# Here short description of this script
# This is just a template to be used for writing new bash scripts
###
# Based on Google Style Guide: https://google.github.io/styleguide/shell.xml
# General remarks
# * Executables should have no extension (strongly preferred) or a .sh extension.
# * Libraries must have a .sh extension and should not be executable
# * SUID and SGID are forbidden on shell scripts.
# * All error messages should go to STDERR.
# * Write todos like this: # TODO(renzok): Handle the unlikely edge cases (bug ####)
# * Indent 2 spaces. No tabs. 80 chars max per line
# * Put ; do and ; then on the same line as the while, for or if.
# * Quoting: https://google.github.io/styleguide/shell.xml#Quoting
# * Function Names: Lower-case, with underscores to separate words.
# ** Separate libraries with ::. Parentheses are required after the function name.
# * prefer shell builtin over separate process
##
##
# Coding tips and tricks:
# http://stackoverflow.com/questions/1167746/how-to-assign-a-heredoc-value-to-a-variable-in-bash
#
# Exit immediately if a command exits with a non-zero status.
#
# This might cause problems e.g. using read to read a heredoc cause
# read to always return non-zero set -o errexit Treat unset variables
# as an error when substituting.
set -o nounset
# 1. section: global constants (all words upper case separated by underscore)
# declare -r CONSTANT_VARIABLE='value'
declare -r TMP_FILE_PREFIX=${TMPDIR:-/tmp}/prog.$$
#declare -r colblk='\033[0;30m' # Black - Regular
declare -r COLOR_RED='\033[0;31m' # Red
declare -r COLOR_GREEN='\033[0;32m' # Green
declare -r COLOR_YELLOW='\033[0;33m' # Yellow
declare -r COLOR_PURPLE='\033[0;35m' # Purple
declare -r COLOR_GRAY='\033[0;90m' # Dark Gray
declare -r COLOR_RESET='\033[0m' # Text Reset
declare verbosity=6
### verbosity levels
declare -ri LEVEL_SILENT=0
declare -ri LEVEL_CRITICAL=1
declare -ri LEVEL_ERROR=2
declare -ri LEVEL_WARN=3
# also for success=ok log message
declare -ri LEVEL_NOTIFY=4
declare -ri LEVEL_INFO=5
declare -ri LEVEL_DEBUG=6
# as per discussion
# http://stackoverflow.com/questions/4774054/reliable-way-for-a-bash-script-to-get-the-full-path-to-itself
# but use BASH_SOURCE[0]
declare -r SCRIPTPATH=$( cd $(dirname ${BASH_SOURCE[0]}) > /dev/null; pwd -P )
# 2. section: functions
# Part of a package/library
function mypackage::my_func() {
}
## esilent prints output even in silent mode
function log::silent () { verb_lvl=$LEVEL_SILENT log "$@" ;}
function log::critical () { verb_lvl=$LEVEL_CRITICAL log "${COLOR_PURPLE}FATAL${COLOR_RESET} --- ${*}" ;}
function log::error () { verb_lvl=$LEVEL_ERROR log "${COLOR_RED}ERROR${COLOR_RESET} --- ${*}" ;}
function log::warn () { verb_lvl=$LEVEL_WARN log "${COLOR_YELLOW}WARNING${COLOR_RESET} - ${*}" ;}
function log::notify () { verb_lvl=$LEVEL_NOTIFY log "${*}" ;}
function log::ok () { verb_lvl=$LEVEL_NOTIFY log "SUCCESS - ${*}" ;}
function log::info () { verb_lvl=$LEVEL_INFO log "${COLOR_GRAY}INFO${COLOR_RESET} ---- ${*}" ;}
function log::debug () { verb_lvl=$LEVEL_DEBUG log "${COLOR_GREEN}DEBUG${COLOR_RESET} --- ${*}" ;}
function log::dumpvar () { for var in "$@" ; do log::debug "$var=${!var}" ; done }
function log() {
if [ $verbosity -ge $verb_lvl ]; then
datestring=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "$datestring - ${*}"
fi
}
# all progs need to be given as parameters
# e.g. _check_required_programs md5 xsltproc
function intern::check_required_programs() {
# Required program(s)
#req_progs=(readlink date md5)
for p in ${@}; do
hash "${p}" 2>&- || \
{ echo >&2 " Required program \"${p}\" not installed or in search PATH.";
exit 1;
}
done
}
function cleanup() {
rm -f ${TMP_FILE_PREFIX}.*
echo "always implement this" && exit 100
}
function usage() {
cat <<EOF
Usage: $0
TODO
EOF
}
# Single function
function main() {
# the optional parameters string starting with ':' for silent errors snd h for help usage
local -r OPTS=':h'
while builtin getopts ${OPTS} opt "${@}"; do
case $opt in
h) usage ; exit 0
;;
\?)
echo ${opt} ${OPTIND} 'is an invalid option' >&2;
usage;
exit ${INVALID_OPTION}
;;
:)
echo 'required argument not found for option -'${OPTARG} >&2;
usage;
exit ${INVALID_OPTION}
;;
*) echo "Too many options. Can not happen actually :)"
;;
esac
done
log::silent "Silent message"
log::critical "CRITICAL MESSAGE!"
log::error "this is an error"
log::warn "this is a warning"
log::ok "super ok"
log::info "this is an information"
log::debug "debugging"
# testing dumpvar function
TEST=test log::dumpvar TEST
cleanup
exit 0
}
# Always check return values and give informative return values.
# see https://google.github.io/styleguide/shell.xml#Checking_Return_Values
# set a trap for (calling) cleanup all stuff before process
# termination by SIGHUBs
trap "cleanup; exit 1" 1 2 3 13 15
# this is the main executable function at end of script
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment