Skip to content

Instantly share code, notes, and snippets.

@aaronNGi
Created April 28, 2020 20:38
Show Gist options
  • Save aaronNGi/a9212f36a8e0c2bc0674e259563ad952 to your computer and use it in GitHub Desktop.
Save aaronNGi/a9212f36a8e0c2bc0674e259563ad952 to your computer and use it in GitHub Desktop.
Boilerplate for new POSIX shell scripts
#!/bin/sh
prog_name=${0##*/}
version=1.0
version_text="Boilerplate for new scripts v$version"
options="h o: q v V"
help_text="Usage: $prog_name [-o <text>] [-hqvV] [<file>]...
Boilerplate for new scripts
-o <text> Set an option with a parameter
-h Display this help text and exit
-q Quiet
-v Verbose mode
-V Display version information and exit"
main() {
set_defaults
parse_options "$@"
shift $((OPTIND-1))
# If we want to use `getopts` again, this has to be set to 1.
OPTIND=1
# shellcheck disable=2154
{
$option_h && usage
$option_V && version
$option_q && info() { :; }
$option_o && info "option 'o' has parameter '$param_o'"
$option_v && info "verbose mode is on"
}
_i=1
for _file do
info "operand $_i is '$_file'"
_i=$((_i+1))
done
unset _i _file
[ -t 0 ] ||
info "stdin is not a terminal"
}
##########################################################################
# shellcheck disable=2034,2046
set_defaults() {
set -e
trap 'clean_exit' EXIT TERM
trap 'clean_exit HUP' HUP
trap 'clean_exit INT' INT
IFS=' '
set -- $(printf '\n \r \t \033')
nl=$1 cr=$2 tab=$3 esc=$4
IFS=\ $tab
}
# For a given optstring, this function sets the variables
# "option_<optchar>" to true/false and param_<optchar> to its parameter.
parse_options() {
for _opt in $options; do
# The POSIX spec does not say anything about spaces in the
# optstring, so lets get rid of them.
_optstring=$_optstring$_opt
eval "option_${_opt%:}=false"
done
while getopts ":$_optstring" _opt; do
case $_opt in
:) usage "option '$OPTARG' requires a parameter" ;;
\?) usage "unrecognized option '$OPTARG'" ;;
*)
eval "option_$_opt=true"
[ -n "$OPTARG" ] &&
eval "param_$_opt=\$OPTARG"
;;
esac
done
unset _opt _optstring OPTARG
}
info() { printf %s\\n "$*" >&2; }
version() { printf %s\\n "$version_text"; exit; }
error() {
_error=${1:-1}
shift
printf '%s: Error: %s\n' "$prog_name" "$*" >&2
exit "$_error"
}
usage() {
[ $# -ne 0 ] && {
exec >&2
printf '%s: %s\n\n' "$prog_name" "$*"
}
printf %s\\n "$help_text"
exit ${1:+1}
}
clean_exit() {
_exit_status=$?
trap - EXIT
info "exiting"
[ $# -ne 0 ] && {
trap - "$1"
kill -s "$1" -$$
}
exit "$_exit_status"
}
main "$@"
@cometsong
Copy link

Love this as a starting point... Thanks much!
Q: is your overall intent to use the set_defaults() to set the main variables' defaults as well? Currently it sets characters as variables and trap defaults... I ask primarily as I've been adding my own variable defaults near the top of my scripts for easier visibility and proximity to the help_text.

@aaronNGi
Copy link
Author

@cometsong I can't reply directly here.

I think in your case I'd add the variables to the top of main().

@epsilon-0
Copy link

is it possible to get parse-opts for longer names options?

@aaronNGi
Copy link
Author

@epsilon-0 not easily. The beauty of parse_options() is that for it's usage you only have to set the scripts options via the options variable and the rest is done for you. Long options will make it more complex in both, usage and code complexity. I'm not a fan of long options in general to say the least, but I will think about a good way (if there is one).

@epsilon-0
Copy link

got it :)

@epsilon-0
Copy link

thanks a lot, I think I am fine without the long options, managed to work around it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment