#!/usr/bin/env bash
# ##################################################
# Bash scripting Utilities.
#
# This script sources my collection of scripting utilities making
# it possible to source this one script and gain access to a
# complete collection of functions, variables, and other options.
#
# Initially taken from here: https://github.com/natelandau/shell-scripts/blob/master/lib/utils.sh
#
# ##################################################
# Logging and Colors
# ------------------------------------------------------
# Here we set the colors for our script feedback.
# Example usage: success "sometext"
#------------------------------------------------------
# Set Colors
bold=$(tput bold)
underline=$(tput sgr 0 1)
reset=$(tput sgr0)
purple=$(tput setaf 171)
red=$(tput setaf 1)
green=$(tput setaf 76)
tan=$(tput setaf 3)
blue=$(tput setaf 38)
function _alert() {
if [ "${1}" = "emergency" ]; then
local color="${bold}${red}"
fi
if [ "${1}" = "error" ]; then local color="${bold}${red}"; fi
if [ "${1}" = "warning" ]; then local color="${red}"; fi
if [ "${1}" = "success" ]; then local color="${green}"; fi
if [ "${1}" = "debug" ]; then local color="${purple}"; fi
if [ "${1}" = "header" ]; then local color="${bold}""${tan}"; fi
if [ "${1}" = "input" ]; then local color="${bold}"; printLog="false"; fi
if [ "${1}" = "info" ] || [ "${1}" = "notice" ]; then local color=""; fi
# Don't use colors on pipes or non-recognized terminals
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi
# Print to $logFile
if [[ ${printLog} = "true" ]] || [ "${printLog}" == "1" ]; then
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%9s]" "${1}") ${_message}" >> "${logFile}";
fi
# Print to console when script is not 'quiet'
if [[ "${quiet}" = "true" ]] || [ "${quiet}" == "1" ]; then
return
else
echo -e "$(date +"%r") ${color}$(printf "[%9s]" "${1}") ${_message}${reset}";
fi
}
function die () { local _message="${*} Exiting."; echo "$(_alert emergency)"; safeExit;}
function error () { local _message="${*}"; echo "$(_alert error)"; }
function warning () { local _message="${*}"; echo "$(_alert warning)"; }
function notice () { local _message="${*}"; echo "$(_alert notice)"; }
function info () { local _message="${*}"; echo "$(_alert info)"; }
function debug () { local _message="${*}"; echo "$(_alert debug)"; }
function success () { local _message="${*}"; echo "$(_alert success)"; }
function input() { local _message="${*}"; echo -n "$(_alert input)"; }
function header() { local _message="========== ${*} ========== "; echo "$(_alert header)"; }
# Log messages when verbose is set to "true"
verbose() {
if [[ "${verbose}" = "true" ]] || [ "${verbose}" == "1" ]; then
debug "$@"
fi
}
# SCRIPTNAME
# ------------------------------------------------------
# Will return the name of the script being run
# ------------------------------------------------------
scriptName=`basename $0` #Set Script Name variable
scriptBasename="$(basename ${scriptName} .sh)" # Strips '.sh' from scriptName
# TIMESTAMPS
# ------------------------------------------------------
# Prints the current date and time in a variety of formats:
#
# ------------------------------------------------------
now=$(LC_ALL=C date +"%m-%d-%Y %r") # Returns: 06-14-2015 10:34:40 PM
datestamp=$(LC_ALL=C date +%Y-%m-%d) # Returns: 2015-06-14
hourstamp=$(LC_ALL=C date +%r) # Returns: 10:34:40 PM
timestamp=$(LC_ALL=C date +%Y%m%d_%H%M%S) # Returns: 20150614_223440
today=$(LC_ALL=C date +"%m-%d-%Y") # Returns: 06-14-2015
longdate=$(LC_ALL=C date +"%a, %d %b %Y %H:%M:%S %z") # Returns: Sun, 10 Jan 2016 20:47:53 -0500
gmtdate=$(LC_ALL=C date -u -R | sed 's/\+0000/GMT/') # Returns: Wed, 13 Jan 2016 15:55:29 GMT
# THISHOST
# ------------------------------------------------------
# Will print the current hostname of the computer the script
# is being run on.
# ------------------------------------------------------
thisHost=$(hostname)
# ------------------------------------------------------
# These functions are for use with different trap scenarios
# ------------------------------------------------------
# Non destructive exit for when script exits naturally.
# Usage: Add this function at the end of every script
function safeExit() {
# Delete temp files, if any
if is_dir "${tmpDir}"; then
rm -r "${tmpDir}"
fi
trap - INT TERM EXIT
exit
}
# readFile
# ------------------------------------------------------
# Function to read a line from a file.
#
# Most often used to read the config files saved in my etc directory.
# Outputs each line in a variable named $result
# ------------------------------------------------------
function readFile() {
unset "${result}"
while read result
do
echo "${result}"
done < "$1"
}
# Escape a string
# ------------------------------------------------------
# usage: var=$(escape "String")
# ------------------------------------------------------
escape() { echo "${@}" | sed 's/[]\.|$(){}?+*^]/\\&/g'; }
# needSudo
# ------------------------------------------------------
# If a script needs sudo access, call this function which
# requests sudo access and then keeps it alive.
# ------------------------------------------------------
function needSudo() {
# Update existing sudo time stamp if set, otherwise do nothing.
sudo -v
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
}
# convertsecs
# ------------------------------------------------------
# Convert Seconds to human readable time
#
# To use this, pass a number (seconds) into the function as this:
# print "$(convertsecs $TOTALTIME)"
#
# To compute the time it takes a script to run use tag the start and end times with
# STARTTIME=$(date +"%s")
# ENDTIME=$(date +"%s")
# TOTALTIME=$(($ENDTIME-$STARTTIME))
# ------------------------------------------------------
function convertsecs() {
((h=${1}/3600))
((m=(${1}%3600)/60))
((s=${1}%60))
printf "%02d:%02d:%02d\n" $h $m $s
}
# Join
# ----------------------------------------------
# This function joins items together with a user specified separator
# Taken whole cloth from: http://stackoverflow.com/questions/1527049/bash-join-elements-of-an-array
#
# Usage:
# join , a "b c" d #a,b c,d
# join / var local tmp #var/local/tmp
# join , "${FOO[@]}" #a,b,c
# ----------------------------------------------
function join() { local IFS="${1}"; shift; echo "${*}"; }
# File Checks
# ------------------------------------------------------
# A series of functions which make checks against the filesystem. For
# use in if/then statements.
#
# Usage:
# if is_file "file"; then
# ...
# fi
# ------------------------------------------------------
function is_exists() {
if [[ -e "$1" ]]; then
return 0
fi
return 1
}
function is_not_exists() {
if [[ ! -e "$1" ]]; then
return 0
fi
return 1
}
function is_file() {
if [[ -f "$1" ]]; then
return 0
fi
return 1
}
function is_not_file() {
if [[ ! -f "$1" ]]; then
return 0
fi
return 1
}
function is_dir() {
if [[ -d "$1" ]]; then
return 0
fi
return 1
}
function is_not_dir() {
if [[ ! -d "$1" ]]; then
return 0
fi
return 1
}
function is_symlink() {
if [[ -L "$1" ]]; then
return 0
fi
return 1
}
function is_not_symlink() {
if [[ ! -L "$1" ]]; then
return 0
fi
return 1
}
function is_empty() {
if [[ -z "$1" ]]; then
return 0
fi
return 1
}
function is_not_empty() {
if [[ -n "$1" ]]; then
return 0
fi
return 1
}
# Test whether a command exists
# ------------------------------------------------------
# Usage:
# if type_exists 'git'; then
# some action
# else
# some other action
# fi
# ------------------------------------------------------
function type_exists() {
if [ "$(type -P "$1")" ]; then
return 0
fi
return 1
}
function type_not_exists() {
if [ ! "$(type -P "$1")" ]; then
return 0
fi
return 1
}
# Test which OS the user runs
# $1 = OS to test
# Usage: if is_os 'darwin'; then
function is_os() {
if [[ "${OSTYPE}" == $1* ]]; then
return 0
fi
return 1
}
# SEEKING CONFIRMATION
# ------------------------------------------------------
# Asks questions of a user and then does something with the answer.
# y/n are the only possible answers.
#
# USAGE:
# seek_confirmation "Ask a question"
# if is_confirmed; then
# some action
# else
# some other action
# fi
#
# Credt: https://github.com/kevva/dotfiles
# ------------------------------------------------------
# Ask the question
function seek_confirmation() {
# echo ""
input "$@"
if "${force}"; then
notice "Forcing confirmation with '--force' flag set"
else
read -p " (y/n) " -n 1
echo ""
fi
}
# Test whether the result of an 'ask' is a confirmation
function is_confirmed() {
if "${force}"; then
return 0
else
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
return 0
fi
return 1
fi
}
function is_not_confirmed() {
if "${force}"; then
return 1
else
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then
return 0
fi
return 1
fi
}
# Skip something
# ------------------------------------------------------
# Offer the user a chance to skip something.
# Credit: https://github.com/cowboy/dotfiles
# ------------------------------------------------------
function skip() {
REPLY=noskip
read -t 5 -n 1 -s -p "${bold}To skip, press ${underline}X${reset}${bold} within 5 seconds.${reset}"
if [[ "$REPLY" =~ ^[Xx]$ ]]; then
notice " Skipping!"
return 0
else
notice " Continuing..."
return 1
fi
}
# unmountDrive
# ------------------------------------------------------
# If an AFP drive is mounted as part of a script, this
# will unmount the volume. This will only work on Macs.
# ------------------------------------------------------
function unmountDrive() {
if [ -d "$1" ]; then
diskutil unmount "$1"
fi
}
# help
# ------------------------------------------------------
# Prints help for a script when invoked from the command
# line. Typically via '-h'. If additional flags or help
# text is available in the script they will be printed
# in the '$usage' variable.
# ------------------------------------------------------
function help () {
echo "" 1>&2
input " $@" 1>&2
if [ -n "${usage}" ]; then # print usage information if available
echo " ${usage}" 1>&2
fi
echo "" 1>&2
exit 1
}
function pauseScript() {
# A simple function used to pause a script at any point and
# only continue on user input
seek_confirmation "Ready to continue?"
if is_confirmed; then
info "Continuing"
else
warning "Exiting Script."
safeExit
fi
}
function in_array() {
# Determine if a value is in an array.
# Usage: if in_array "VALUE" "${ARRAY[@]}"; then ...
local value="$1"; shift
for arrayItem in "$@"; do
[[ "${arrayItem}" == "${value}" ]] && return 0
done
return 1
}
# Text Transformations
# -----------------------------------
# Transform text using these functions.
# Adapted from https://github.com/jmcantrell/bashful
# -----------------------------------
lower() {
# Convert stdin to lowercase.
# usage: text=$(lower <<<"$1")
# echo "MAKETHISLOWERCASE" | lower
tr '[:upper:]' '[:lower:]'
}
upper() {
# Convert stdin to uppercase.
# usage: text=$(upper <<<"$1")
# echo "MAKETHISUPPERCASE" | upper
tr '[:lower:]' '[:upper:]'
}
ltrim() {
# Removes all leading whitespace (from the left).
local char=${1:-[:space:]}
sed "s%^[${char//%/\\%}]*%%"
}
rtrim() {
# Removes all trailing whitespace (from the right).
local char=${1:-[:space:]}
sed "s%[${char//%/\\%}]*$%%"
}
trim() {
# Removes all leading/trailing whitespace
# Usage examples:
# echo " foo bar baz " | trim #==> "foo bar baz"
ltrim "$1" | rtrim "$1"
}
squeeze() {
# Removes leading/trailing whitespace and condenses all other consecutive
# whitespace into a single space.
#
# Usage examples:
# echo " foo bar baz " | squeeze #==> "foo bar baz"
local char=${1:-[[:space:]]}
sed "s%\(${char//%/\\%}\)\+%\1%g" | trim "$char"
}
squeeze_lines() {
# {{{
#
# Removes all leading/trailing blank lines and condenses all other
# consecutive blank lines into a single blank line.
#
# }}}
sed '/^[[:space:]]\+$/s/.*//g' | cat -s | trim_lines
}
progressBar() {
# progressBar
# -----------------------------------
# Prints a progress bar within a for/while loop.
# To use this function you must pass the total number of
# times the loop will run to the function.
#
# usage:
# for number in $(seq 0 100); do
# sleep 1
# progressBar 100
# done
# -----------------------------------
if [[ "${quiet}" = "true" ]] || [ "${quiet}" == "1" ]; then
return
fi
local width
width=30
bar_char="#"
# Don't run this function when scripts are running in verbose mode
if ${verbose}; then return; fi
# Reset the count
if [ -z "${progressBarProgress}" ]; then
progressBarProgress=0
fi
# Do nothing if the output is not a terminal
if [ ! -t 1 ]; then
echo "Output is not a terminal" 1>&2
return
fi
# Hide the cursor
tput civis
trap 'tput cnorm; exit 1' SIGINT
if [ ! "${progressBarProgress}" -eq $(( $1 - 1 )) ]; then
# Compute the percentage.
perc=$(( progressBarProgress * 100 / $1 ))
# Compute the number of blocks to represent the percentage.
num=$(( progressBarProgress * width / $1 ))
# Create the progress bar string.
bar=
if [ ${num} -gt 0 ]; then
bar=$(printf "%0.s${bar_char}" $(seq 1 ${num}))
fi
# Print the progress bar.
progressBarLine=$(printf "%s [%-${width}s] (%d%%)" "Running Process" "${bar}" "${perc}")
echo -en "${progressBarLine}\r"
progressBarProgress=$(( progressBarProgress + 1 ))
else
# Clear the progress bar when complete
echo -ne "${width}%\033[0K\r"
unset progressBarProgress
fi
tput cnorm
}
htmlDecode() {
# Decode HTML characters with sed
# Usage: htmlDecode
echo "${1}" | sed -f "${SOURCEPATH}/htmlDecode.sed"
}
htmlEncode() {
# Encode HTML characters with sed
# Usage: htmlEncode
echo "${1}" | sed -f "${SOURCEPATH}/htmlEncode.sed"
}
urlencode() {
# URL encoding/decoding from: https://gist.github.com/cdown/1163649
# Usage: urlencode
local length="${#1}"
for (( i = 0; i < length; i++ )); do
local c="${1:i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf "%s" "$c" ;;
*) printf '%%%02X' "'$c"
esac
done
}
urldecode() {
# Usage: urldecode
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\x}"
}
parse_yaml() {
# Function to parse YAML files and add values to variables. Send it to a temp file and source it
# https://gist.github.com/DinoChiesa/3e3c3866b51290f31243 which is derived from
# https://gist.github.com/epiloque/8cf512c6d64641bde388
#
# Usage:
# $ parse_yaml sample.yml > /some/tempfile
#
# parse_yaml accepts a prefix argument so that imported settings all have a common prefix
# (which will reduce the risk of name-space collisions).
#
# $ parse_yaml sample.yml "CONF_"
local prefix=$2
local s
local w
local fs
s='[[:space:]]*'
w='[a-zA-Z0-9_]*'
fs="$(echo @|tr @ '\034')"
sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)$s[:-]$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$1" |
awk -F"$fs" '{
indent = length($1)/2;
if (length($2) == 0) { conj[indent]="+";} else {conj[indent]="";}
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i> "${csvFile}"
IFS=$saveIFS
}
function json2yaml() {
# convert json files to yaml using python and PyYAML
python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' < "$1"
}
function yaml2json() {
# convert yaml files to json using python and PyYAML
python -c 'import sys, yaml, json; json.dump(yaml.load(sys.stdin), sys.stdout, indent=4)' < "$1"
}