Skip to content

Instantly share code, notes, and snippets.

@e-picas
Last active January 27, 2023 09:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save e-picas/ee576171255463dbcc599454414ba8a5 to your computer and use it in GitHub Desktop.
Save e-picas/ee576171255463dbcc599454414ba8a5 to your computer and use it in GitHub Desktop.
A Bash script model with common options (verbose, force etc) & minimal functions (error, usage etc) to use and re-use to build your own shell scripts easily.
#!/usr/bin/env bash
#
# Bash script model
#
# A Bash script model with common options (verbose, force etc)
# & minimal functions (error, usage etc)
# to use and re-use to build your own shell scripts easily.
#
# To begin, run one of the followings:
#
# ./shell-script-model.sh -h
# ./shell-script-model.sh --help
# ./shell-script-model.sh -x
#
# For last updates, see <https://gist.github.com/ee576171255463dbcc599454414ba8a5.git>
#
# bash options
#set -e
set -u
set -o pipefail
# edit the `SCRIPT_...` variables below to adapt to your script
SCRIPT_VERSION="0.0.1-dev"
SCRIPT_NAME="$0"
SCRIPT_PRESENTATION="This script is a bash script model."
IFS='' read -r -d '' SCRIPT_USAGE <<EOF
usage: $0 [-h | --help | -v/--verbose | -f/--force] [options [=value]] <arguments>
i.e.: $0 test
options:
-h see a short usage string and exit with status 0
--help see a long help string and exit with status 0
-v|--verbose enable verbosity
-f|--force enable force
EOF
# This model is designed to use the followings by default:
#
# - the `-h` option shows a simple "usage" string and exits with status 0 (no error)
# - the `--help` option shows a long "help" string and exits with status 0 (no error)
# - the `-v` or `--verbose` options enables the `VERBOSE` boolean variable
# - the `-f` or `--force` options enables the `FORCE` boolean variable
# - the `-x` option enables the `DEBUG` boolean variable (for development)
#
SCRIPT_OPTS_SHORT='fhvx'
SCRIPT_OPTS_LONG='force,help,verbose'
export SCRIPT_VERSION SCRIPT_NAME SCRIPT_PRESENTATION SCRIPT_USAGE SCRIPT_OPTS_SHORT SCRIPT_OPTS_LONG
# environment
VERBOSE=false
FORCE=false
DEBUG=false
declare -a DEBUG_DATA
# internal tracelog function
traceback () {
local -i start=$(( ${1:-0} + 1 ))
local -i end=${#BASH_SOURCE[@]}
local -i i=0
local -i j=0
echo " Traceback (last call is first):" 1>&2
for ((i=${start}; i < ${end}; i++)); do
j=$(( $i - 1 ))
local function="${FUNCNAME[$i]}"
local file="${BASH_SOURCE[$i]}"
local line="${BASH_LINENO[$j]}"
echo " # ${function}() in ${file}:${line}" 1>&2
done
}
# usage: die <error string> [traces nb=1]
die () {
{ echo '---'
echo "!! > $1"
traceback "${2:-1}"
echo '---'
} 1>&2
exit 2
}
# usage: get_absolute_path <path>
get_absolute_path() {
[ $# -eq 0 ] && die 'usage: get_absolute_path <path>';
local cwd="$(pwd)"
local path="$1"
while [ -n "$path" ]; do
cd "${path%/*}" 2>/dev/null;
local name="${path##*/}"
path="$($(type -p greadlink readlink | head -1) "$name" || true)"
done
pwd
cd "$cwd"
}
# usage: usage
usage () {
echo "${SCRIPT_USAGE}"
}
# usage: help
help () {
cat <<EOT
## ${SCRIPT_NAME} - ${SCRIPT_VERSION} ##
${SCRIPT_PRESENTATION}
${SCRIPT_USAGE}
EOT
}
# usage: error [str='']
error () {
{ echo '---'
echo "> $*"
echo '---'
usage
} 1>&2
exit 1
}
# usage: verbose_echo <string to print if verbosity is on>
verbose_echo () {
$VERBOSE && echo "$*";
}
# library dir
BASEDIR="$(get_absolute_path "${BASH_SOURCE[0]}")"
# arguments
PARSED_OPTS=$(
getopt --name "$0" --options "$SCRIPT_OPTS_SHORT" --longoptions "$SCRIPT_OPTS_LONG" -- "$@"
) || error "script parameters error!";
eval "set -- $PARSED_OPTS";
for opt in "$@"; do
case "$opt" in
-f|--force)
FORCE=true
shift
;;
-h)
usage
exit 0
;;
--help)
help
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
-x)
DEBUG=true
shift
;;
--)
shift
continue
;;
esac
done
export VERBOSE FORCE DEBUG DEBUG_DATA
#### command starts here... ####
# some tests
echo 'YO'
verbose_echo 'YO (verbose)'
DEBUG_DATA+=( test1 )
DEBUG_DATA+=( test2 )
# loop over user arguments
for i in "$@"; do
echo "> $i"
done
#### command ends here... ####
# debug environment
$DEBUG && (
{ echo "[dbg] ////"
echo "[dbg] > CALL REBUILT : $*"
echo "[dbg] > VERBOSE : $VERBOSE"
echo "[dbg] > FORCE : $FORCE"
echo "[dbg] > DEBUG : $DEBUG"
echo "[dbg] > DEBUG DATA : ${DEBUG_DATA[@]}"
echo "[dbg] ////"
} 1>&2
);
# exit status
exit 0
# vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment