Skip to content

Instantly share code, notes, and snippets.

@kesor

kesor/utils.sh

Created Jul 1, 2020
Embed
What would you like to do?
example utils.sh
#!/bin/bash
# Common utility functions for bash scripts,
# - fatal -- use like `some_cmd || fatal "Some cmd failed"`
# - debug -- use like `debug "This is a debug message"`
# - timer -- use like `tm some_cmd` as a wrapper
# - retry -- use like `retry some_cmd` as a wrapper, retries with exp. backoff
#
# Source this bash like so: `. util.sh`
#
# *** Requires `jq` to be installed.
#
# Debug and timer is only printed when DEBUG=1
# Output is printed as JSON when JSON_OUTPUT=1
#
# echo message to stderr and exit process with non-zero return code
fatal() {
# exit process with an error message
# use json syntax when JSON_OUTPUT is defined
rc=$?
if [ -z "$JSON_OUTPUT" ]; then
echo "E $(date -u +%s%3N) ${BASH_SOURCE[1]}:${BASH_LINENO[1]}:${FUNCNAME[1]}:error:$rc: $*" >&2
else
echo "{\"type\":\"fatal\",\"time\":\"$(date -u +%s%3N)\",\"source\":\"${BASH_SOURCE[1]}\",\"line\":\"${BASH_LINENO[1]}\",\"func\":\"${FUNCNAME[1]}\",\"code\":\"$rc\",\"message\":\"$*\"}" \
| jq -Sc . >&2
fi
exit $rc
}
# echo message to stderr when DEBUG=1
debug() {
# print a debug message to stderr when DEBUG is defined;
# use json syntax when JSON_OUTPUT is defined
[ -z "$DEBUG" ] && return
if [ -z "$JSON_OUTPUT" ]; then
echo "D $(date -u +%s%3N) ${BASH_SOURCE[1]}:${BASH_LINENO[1]}:${FUNCNAME[1]}:debug: $*" >&2
else
echo "{\"type\":\"debug\",\"time\":\"$(date -u +%s%3N)\",\"source\":\"${BASE_SOURCE[1]}\",\"line\":\"${BASH_LINENO[1]}\",\"func\":\"${FUNCNAME[1]}\",\"message\":\"$*\"}" \
| jq -Sc . >&2
fi
}
# measure time duration of executed process and echo when DEBUG=1
tm() {
# execute a command and print its timing to stderr
# use json syntax when JSON_OUTPUT is defined
# in json format, capture the command stdout and stderr into json
[ -z "$DEBUG" ] && { "$@"; return $?; }
START=$(date +%s%3N)
if [ -z "$JSON_OUTPUT" ]; then
"$@"; rc=$?
END=$(date +%s%3N)
echo "T $(date -u +%s%3N) ${BASH_SOURCE[1]}:${BASH_LINENO[1]}:${FUNCNAME[1]}:$(( END - START ))ms: $*" >&2
else
stdout_pipe=$(mktemp -u)
stderr_pipe=$(mktemp -u)
trap 'cat $stdout_pipe >/dev/null 2>&1; cat $stderr_pipe >/dev/null 2>&1; rm -f $stdout_pipe $stderr_pipe' RETURN
"$@" 1>"$stdout_pipe" 2>"$stderr_pipe"; rc=$?
END=$(date +%s%3N)
echo "{\"type\":\"time\",\"time\":\"$(date -u +%s%3N)\",\"source\":\"${BASE_SOURCE[1]}\",\"line\":\"${BASH_LINENO[1]}\",\"func\":\"${FUNCNAME[1]}\",\"ms\":\"$(( END - START ))\",\"cmd\":\"$*\"}" \
| jq -Sc --arg stdout "$(cat "$stdout_pipe")" --arg stderr "$(cat "$stderr_pipe")" '. * { "stdout": $stdout, "stderr": $stderr }' >&2
fi
return $rc
}
retry() {
max_retries=${MAX:-5}
retries=0
until tm "$@" && [ "$retries" -lt "$max_retries" ]; do
duration=$(( 2 ** retries ))
retries=$(( retries + 1 ))
debug "Retrying after ${duration}s"
sleep "$duration"
done
fatal "Maximum retries ($MAX) reached. Could not perform $*"
}
# check that script is being executed by root, or abort
check_root() {
[ "$(id -u)" != '0' ] && \
fatal "Must run $0 as root or using sudo."
}
# check that the docker command exists, or abort
check_docker() {
command -v docker >/dev/null \
|| fatal "==> Could not find Docker installed."
}
mkdir_chown() {
debug "Creating directories & changing to root ownership: $*"
mkdir -p "$@"
chown -R 0:0 "$@"
}
# join multiple strings into a long string separated by first arg. value
strings_join() {
local IFS="$1"
shift
echo "$*"
}
eval_os_release() {
# set environment variables OS_ID and OS_VERSION_ID from Linux release info
## possible OS_ID values:
# ubuntu debian rhel centos fedora amazon
eval "$(sed -ne '/^\(VERSION_ID\|ID\)=/s!^!export OS_!gp' /etc/os-release)"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment