#!/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" }