Skip to content

Instantly share code, notes, and snippets.

@jaytaylor
Last active February 5, 2024 17:21
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 jaytaylor/8f7e93f7c235367e20f0cae116bcf4c0 to your computer and use it in GitHub Desktop.
Save jaytaylor/8f7e93f7c235367e20f0cae116bcf4c0 to your computer and use it in GitHub Desktop.
jaytaylor's Bash shell skeleton programming snippets quick reference
#!/usr/bin/env bash
main() {
printf '%s\n' "INFO: $(basename "$0"):${FUNCNAME[0]}:${LINENO}: Hello world" 1>&2
}
export -f main
if [ "${BASH_SOURCE[0]}" = "${0}" ] || [ "${BASH_SOURCE[0]}" = '--' ]; then
set -o errexit
set -o pipefail
set -o nounset
if [ "${1:-}" = '-v' ]; then
printf '%s\n' "INFO: $(basename "$0")::${LINENO}: Verbose output enabled" 1>&2
shift
set -o xtrace
fi
main "$@"
fi
#!/usr/bin/env bash
_ts() { date -u +'%Y-%m-%dT%H:%M:%S%z'; }
log_debug() { printf '%s\n' "$(_ts) DEBUG: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2; }
log_info() { printf '%s\n' "$(_ts) INFO: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2; }
log_warn() { printf '%s\n' "$(_ts) WARN: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2; }
log_error() { printf '%s\n' "$(_ts) ERROR: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2; }
die() { log_error "$*"; exit 1; }
export -f _ts
export -f log_debug
export -f log_info
export -f log_warn
export -f log_error
export -f die
# n.b. alternate one-liner hack form:
# eval "$(for x in info warn error; do echo -e "log_${x}() { echo \"\$(_ts) $(echo ${x} | tr [:lower:] [:upper:]): \$*\"; };"; done)"
usage() { printf '%s\n' "usage: $0 [-v?] ..."; }
main() {
if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then
usage
return 0
fi
}
if [ "${BASH_SOURCE[0]}" = "${0}" ] || [ "${BASH_SOURCE[0]}" = '--' ]; then
set -o errexit
set -o pipefail
set -o nounset
if [ "${1:-}" = '-v' ]; then
log_debug 'verbose mode enabled'
shift
set -o xtrace
fi
main "$@"
fi
#!/usr/bin/env bash
_ts() {
date -u +'%Y-%m-%dT%H:%M:%S%z'
}
export -f _ts
log_debug() {
printf '%s\n' "$(_ts) DEBUG: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2
}
export -f log_debug
log_info() {
printf '%s\n' "$(_ts) INFO: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2
}
export -f log_info
log_warn() {
printf '%s\n' "$(_ts) WARN: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2
}
export -f log_warn
log_error() {
printf '%s\n' "$(_ts) ERROR: $(basename "$0"):$(caller 0 | awk '{ print $2 ":" $1 }') $*" 1>&2
}
export -f log_error
die() {
log_error "$*"
exit 1
}
export -f die
#export -f _ts
#export -f log_debug
#export -f log_info
#export -f log_warn
#export -f log_error
#export -f die
# n.b. alternate one-liner hack form:
# eval "$(for x in info warn error; do echo -e "log_${x}() { echo \"\$(_ts) $(echo ${x} | tr [:lower:] [:upper:]): \$*\"; };"; done)"
usage() {
echo "usage: $0 [-v?] ..."
}
main() {
if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then
usage
return 0
fi
}
# TODO: Revisit this, behavior seems different from what I observed on
# Kubernetes last week. Currently not working properly.
## n.b. Checking for '--' will activate main in cases where the script body has
## been passed in as a string argument.
##
## Example:
## bash -c 'echo "args=\"$@\" BASH_SOURCE[0]=\""${BASH_SOURCE}"\""' -- -v
if [ "${BASH_SOURCE[0]}" = "${0}" ] || [ "${BASH_SOURCE[0]}" = '--' ]; then
set -o errexit
set -o pipefail
set -o nounset
if [ "${1:-}" = '-v' ]; then
printf '%s\n' 'INFO: Verbose output enabled' 1>&2
shift
set -o xtrace
fi
main "$@"
fi
#!/usr/bin/env bash
die() {
printf '%s\n' "ERROR: $*" 1>&2
exit 1
}
export -f die
usage() {
printf '%s\n' "usage: $0 [-v?] ..."
}
main() {
if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then
usage
return 0
fi
}
if [ "${BASH_SOURCE[0]}" = "${0}" ] || [ "${BASH_SOURCE[0]}" = '--' ]; then
set -o errexit
set -o pipefail
set -o nounset
if [ "${1:-}" = '-v' ]; then
printf '%s\n' 'INFO: Verbose output enabled' 1>&2
shift
set -o xtrace
fi
main "$@"
fi

Bash verbose xtrace mode

if [ "${1:-}" = '-v' ]; then
    printf '%s\n' 'DEBUG: verbose mode enabled' 1>&2
    set -o xtrace
    # n.b. trap is probably pointless here.  Use at your own discretion.
    trap 'set +o xtrace' EXIT
    shift
fi

Fancy log messaging with source file, function name, and line number incorporated.

# Logging message which includes the source file, function name, and line number.
printf '%s\n' "DEBUG: $(basename "${BASH_SOURCE}"):${FUNCNAME[0]}:${LINENO}: This is a log message" 1>&2

Obtain script directory

BASEPATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

cd "${BASEPATH}"

Bash main()

Bash equivalent to if __name__ == '__main__': in Python.

n.b. The '--' part needs to be revisited. Yesterday (Sunday 2020-02-09) I was unable to reproduce the behavior previously observed in Kubernetes a few weeks ago.

if [ "${BASH_SOURCE[0]}" = "${0}" ] || [ "${BASH_SOURCE[0]}" = '--' ]; then
    main "$@"
fi

Bash pop last argument / reverse_shift

This can also be thought of as a reverse of the shift command.

Works for both scripts and functions.

Credit: Source

last="${@:$#}"

set -- "${@:1:$(($#-1))}"

trappend: Set or append command to trap signal handler

# trappend is like trap, with the addition  that if there is an existing trap
# already set, it will append the new command(s) without clobbering the
# pre-existing trap orders.
#
# n.b. Won't work for RETURN (hopefully this is somewhat obvious ;).
#
# usage: trappend cmds.. SIGNAL
trappend() {
    local sig
    local existing

    # n.b. Reverse-shift operation.
    sig="${*:$#}"
    set -- "${@:1:$(($#-1))}"

    if [ "${sig}" = 'RETURN' ]; then
        echo 'ERROR: trappend: SIGNAL value cannot be "RETURN"' 1>&2
        return 1
    fi

    if [ -n "$(trap -p "${sig}")" ]; then
        existing="$(trap -p "${sig}" | sed "s/^trap -- '\(.*\)' ${sig}\$/\1/");"
    fi

    # shellcheck disable=SC2064
    trap "${existing:-}$*" "${sig}"
}

Example usage

trappend 'echo hello 3' EXIT
trappend 'echo hello 4' EXIT

echo hello 0
echo hello 1

Output:

hello 0
hello 1
hello 3
hello 4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment