Skip to content

Instantly share code, notes, and snippets.

@vguarnaccia
Last active April 26, 2018 17:49
Show Gist options
  • Save vguarnaccia/6065d725e50f772bd940a174e1b87594 to your computer and use it in GitHub Desktop.
Save vguarnaccia/6065d725e50f772bd940a174e1b87594 to your computer and use it in GitHub Desktop.
Skeleton of a shell script.
#! /bin/sh
readonly -p VERSION=0.1.0
##############################################################################
# This is an example shell script. I do my best to avoid bashisms when it is
# not too much of a pain to do so. I also try to stick to a 80 char line limit
# and indent with tabs since tabs are easier to work with in `sh` than spaces.
# If your script contains calculations or complex logic, please consider
# writting it in a modern scripting language such as python.
# Inspired by https://dev.to/thiht/shell-scripts-matter and
# http://redsymbol.net/articles/unofficial-bash-strict-mode/#issues-and-solutions
# Make sure to lint with `shfmt -w example.sh && shellcheck example.sh`
# You learn more about shellcheck rules on the wiki.
# https://github.com/koalaman/shellcheck/wiki
# Lint with `$ shfmt -w example.sh && shellcheck example.sh`
# Test scripts with shunit2 and put in version control.
##############################################################################
# Debug with `$ sh -x example.sh`
set -euo pipefail # stop if any failures
# Do not accept spaces as Internal Field Separator.
# Equivalent to IFS=$'\t\n' in Bash.
# Also, use `$@` instead of `$*`
safe_IFS="$(printf '%b_' '\t\n')"
IFS="${safe_IFS%_}" # protect trailing \n
##############################################################################
# Helper functions.
info() {
if [ "$VERBOSE" -gt 0 ]; then
echo "[INFO] " "$@" | tee -a "$LOG_FILE" >&2
else
echo "[INFO] " "$@" >>"$LOG_FILE"
fi
}
error() {
echo "[ERROR] " "$@" | tee -a "$LOG_FILE" >&2
exit 1
}
heredoc() {
cat <<-EOM
Usage:
This is an example template of a shell script. It reads
parameters from the command line and prints them when run verbosely.
Example:
./example.sh -p $PWD/file.txt
EOM
}
##############################################################################
# Define variables here.
# Make sure to source secrets from a `.env` instead of passing secrets though
# you may need allow unbound variables when sourcing other people's scripts.
# `$ . example_secrets.env # OK!`
# `$ set +u; . functions.sh; set -u # allow badness in sourc'ed scripts.`
tmp_dir=$(mktemp -d)
export tmp_dir
# Make sure to release any resources we acquired.
tear_down() {
# sudo systemctl restart important.service
rm -rf "$tmp_dir"
}
##############################################################################
# Parse command line arguments.
# Switch statement (slightly) adapted from
# https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash/14203146#14203146
VERBOSE=0
LOG_FILE="/tmp/$(basename "$0").log"
while [ $# -gt 0 ]; do
key="$1"
case $key in
-V | --version)
echo Version: "$VERSION"
shift
exit 0
;;
-h | --help)
heredoc
exit 0
shift
;;
-p | --positional)
POSITIONAL="$2"
shift # past argument
shift # past value
;;
-o | --optional)
OPTIONAL="$2"
shift # past argument
shift # past value
;;
--flag)
FLAG='YES'
shift # past argument
;;
--nolog)
LOG_FILE=/dev/null
shift
;;
-v | --verbose)
VERBOSE=1
shift
;;
*)
error "$1 is not a valid option."
shift
;;
esac
done
readonly -p POSITIONAL
readonly -p OPTIONAL="${OPTIONAL:-default value}" # set default
readonly -p FLAG="${FLAG:-NO}"
readonly -p LOG_FILE
readonly -p VERBOSE
# Write your functions here.
main() {
info The positional value is = "${POSITIONAL}"
info The optional argument is = "${OPTIONAL}"
info The flag is = "${FLAG}"
}
##############################################################################
date >>"$LOG_FILE"
date +"%Y-%m-%dT%H:%M:%S%z" >>"$LOG_FILE"
main
trap tear_down EXIT # Always call tear_down
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment