#! /usr/bin/env bash # ___________________________________________________________________________ # # # # BashLIB -- A library for Bash scripting convenience. # # # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http://www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # # ___________________________________________________________________________ # # # # # # Copyright 2007-2010, lhunath # # * http://www.lhunath.com # # * Maarten Billemont # # # # ______________________________________________________________________ # | | # | .:: GLOBAL CONFIGURATION ::. | # |______________________________________________________________________| { shopt -s extglob shopt -s globstar } 2>/dev/null ||: # Unset all exported functions. Exported functions are evil. while read _ _ func; do unset -f "$func" done < <(declare -Fx) # ______________________________________________________________________ # | | # | .:: GLOBAL DECLARATIONS ::. | # |______________________________________________________________________| # Variables for global internal operation. bobber=( '.' 'o' 'O' 'o' ) spinner=( '-' \\ '|' '/' ) crosser=( '+' 'x' '+' 'x' ) runner=( '> >' \ '>> ' \ '>>>' \ ' >>' ) # Variables for terminal requests. [[ -t 1 ]] && { hide=$( tput civis || tput vi ) show=$( tput cvvis || tput vs ) save=$( tput sc ) load=$( tput rc ) bold=$( tput bold || tput md ) reset=$( tput sgr0 || tput me ) #blink=$( tput blink || tput mb ) italic=$( tput sitm || tput ZH ) [[ $TERM != *-m ]] && { red=$( tput setaf 1|| tput AF 1 ) green=$( tput setaf 2|| tput AF 2 ) yellow=$( tput setaf 3|| tput AF 3 ) blue=$( tput setaf 4|| tput AF 4 ) magenta=$( tput setaf 5|| tput AF 5 ) cyan=$( tput setaf 6|| tput AF 6 ) } white=$( tput setaf 7|| tput AF 7 ) default=$( tput op ) eed=$( tput ed || tput cd ) # Erase to end of display eel=$( tput el || tput ce ) # Erase to end of line ebl=$( tput el1 || tput cb ) # Erase to beginning of line ewl=$eel$ebl # Erase whole line draw=$( tput -S <<< ' enacs smacs acsc rmacs' || { \ tput eA; tput as; tput ac; tput ae; } ) # Drawing characters back=$'\b' } 2>/dev/null ||: # ______________________________________________________________________ # | | # | .:: FUNCTION DECLARATIONS ::. | # |______________________________________________________________________| # ______________________________________________________________________ # |__ Chr _______________________________________________________________| # # chr decimal # # Outputs the character that has the given decimal ASCII value. # chr() { printf \\"$(printf '%03o' "$1")" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Ord _______________________________________________________________| # # ord character # # Outputs the decimal ASCII value of the given character. # ord() { printf %d "'$1" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Hex _______________________________________________________________| # # hex character # # Outputs the hexadecimal ASCII value of the given character. # hex() { printf '%x' "'$1" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Unhex _______________________________________________________________| # # unhex character # # Outputs the character that has the given decimal ASCII value. # unhex() { printf \\x"$1" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Exists ____________________________________________________________| # # exists application # # Returns successfully if the application is in PATH and is executable # by the current user. # exists() { [[ -x $(type -P "$1" 2>/dev/null) ]] } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Eol _______________________________________________________________| # # eol message # # Return termination punctuation for a message, if necessary. # eol() { : #[[ $1 && $1 != *[\!\?.,:\;\|] ]] && printf .. ||: } # _____________________________________________________________________ # ______________________________________________________________________ # |__ CLoc ______________________________________________________________| # # cloc # # Outputs the current cursor location as two space-separated numbers: row column # cloc() { local old=$(stty -g) stty raw # If the tty has input waiting then we can't read back its response. We'd only break and pollute the tty input buffer. read -t 0 < /dev/tty 2>/dev/null && { stty "$old" return 1 } printf '\e[6n' > /dev/tty IFS='[;' read -dR _ row col < /dev/tty printf '%d %d' "$row" "$col" stty "$old" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Latest ____________________________________________________________| # # latest [file...] # # Output the argument that represents the file with the latest modification time. # latest() ( shopt -s nullglob local file latest=$1 for file; do [[ $file -nt $latest ]] && latest=$file done printf '%s\n' "$latest" ) # ______________________________________________________________________ # |__ Emit ______________________________________________________________| # # emit [options] message... [-- [command args...]] # # Display a message with contextual coloring. # # When a command is provided, a spinner will be activated in front of the # message for as long as the command runs. When the command ends, its # exit status will result in a message 'done' or 'failed' to be displayed. # # It is possible to only specify -- as final argument. This will prepare # a spinner for you with the given message but leave it up to you to # notify the spinner that it needs to stop. See the documentation for # 'spinner' to learn how to do this. # # -n Do not end the line with a newline. # -b Activate bright (bold) mode. # -d Activate half-bright (dim) mode. # -g Display in green. # -y Display in yellow. # -r Display in red. # -w Display in the default color. # # -[code] A proxy-call to 'spinner -[code]'. # # Non-captialized versions of these options affect the * or the spinner # in front of the message. Capitalized options affect the message text # displayed. # emit() { # Proxy call to spinner. [[ $# -eq 1 && $1 = -+([0-9]) ]] \ && { spinner $1; return; } # Initialize the vars. local arg local style= local color= local textstyle= local textcolor= local noeol=0 local cmd=0 # Parse the options. spinArgs=() for arg in $(getArgs odbwgyrDBWGYRn "$@"); do case ${arg%% } in d) style=$dim ;; b) style=$bold ;; w) color=$white ;; g) color=$green ;; y) color=$yellow ;; r) color=$red ;; D) textstyle=$dim ;; B) textstyle=$bold ;; W) textcolor=$white ;; G) textcolor=$green ;; Y) textcolor=$yellow ;; R) textcolor=$red ;; n) noeol=1 spinArgs+=(-n) ;; o) spinArgs+=("-$arg") ;; esac done shift $(getArgs -c odbwgyrDBWGYRn "$@") while [[ $1 = +* ]]; do spinArgs+=("-${1#+}") shift done # Defaults. color=${color:-$textcolor} color=${color:-$green} [[ $color = $textcolor && -z $style ]] && style=$bold # Get the text message. local text= origtext= for arg; do [[ $arg = -- ]] && break; origtext+="$arg "; done origtext=${origtext%% } (( noeol )) && text=$origtext || text=$origtext$reset$(eol "$origtext")$'\n' # Trim off everything up to -- while [[ $# -gt 1 && $1 != -- ]]; do shift; done [[ $1 = -- ]] && { shift; cmd=1; } # Figure out what FD to use for our messages. [[ -t 1 ]]; local fd=$(( $? + 1 )) # Display the message or spinner. if (( cmd )); then # Don't let this Bash handle SIGINT. #trap : INT # Create the spinner in the background. spinPipe=${TMPDIR:-/tmp}/bashlib.$$ { touch "$spinPipe" && rm -f "$spinPipe" && mkfifo "$spinPipe"; } 2>/dev/null \ || unset spinPipe { spinner "${spinArgs[@]}" "$origtext" -- "$style" "$color" "$textstyle" "$textcolor" < "${spinPipe:-/dev/null}" & } 2>/dev/null [[ $spinPipe ]] && echo > "$spinPipe" spinPid=$! # Execute the command for the spinner if one is given. #sleep 1 # Let the spinner initialize itself properly first. # Can probably remove this now that we echo > spinPipe? if (( $# == 1 )); then command=$1 elif (( $# > 1 )); then command=$(printf '%q ' "$@") else return 0; fi eval "$command" >/dev/null \ && spinner -0 \ || spinner -1 else # Make reset codes restore the initial font. local font=$reset$textstyle$textcolor text=$font${text//$reset/$font} printf "\r$reset $style$color* %s$reset" "$text" >&$fd fi } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Spinner ___________________________________________________________| # # spinner message... [-- style color textstyle textcolor] # or # spinner -[code] # # Displays a spinner on the screen that waits until a certain time. # Best used through its interface provided by 'emit'. # # style A terminal control string that defines the style of the spinner. # color A terminal control string that defines the color of the spinner. # textstyle A terminal control string that defines the style of the message. # textcolor A terminal control string that defines the color of the message. # # -[code] Shut down a previously activated spinner with the given exit # code. If the exit code is 0, a green message 'done' will be # displayed. Otherwise a red message 'failed' will appear. # The function will return with this exit code as result. # # You can manually specify a previously started spinner by putting its PID in # the 'spinPid' variable. If this variable is not defined, the PID of the most # recently backgrounded process is used. The 'spinPid' variable is unset upon # each call to 'spinner' and reset to the PID of the spinner if one is created. # spinner() { # Check usage. (( ! $# )) || getArgs -q :h "$@" && { emit -y 'Please specify a message as argument or a status option.' return 1 } # Initialize spinner vars. # Make sure monitor mode is off or we won't be able to trap INT properly. local monitor=0; [[ $- = *m* ]] && monitor=1 local done= # Place the trap for interrupt signals. trap 'done="${red}failed"' USR2 trap 'done="${green}done"' USR1 # Initialize the vars. local pid=${spinPid:-$!} local graphics=( "${bobber[@]}" ) local style=$bold local color=$green local textstyle= local textcolor= local output= local noeol= unset spinPid # Any remaining options are the exit status of an existing spinner or spinner type. while [[ $1 = -* ]]; do arg=${1#-} shift # Stop parsing when arg is -- [[ $arg = - ]] && break # Process arg: Either a spinner type or result code. if [[ $arg = *[^0-9]* ]]; then case $arg in b) graphics=( "${bobber[@]}" ) ;; c) graphics=( "${crosser[@]}" ) ;; r) graphics=( "${runner[@]}" ) ;; s) graphics=( "${spinner[@]}" ) ;; o) output=1 ;; n) noeol=1 ;; esac elif [[ $pid ]]; then [[ $arg = 0 ]] \ && kill -USR1 $pid 2>/dev/null \ || kill -USR2 $pid 2>/dev/null trap - INT wait $pid 2>/dev/null return $arg fi done # Read arguments. local text= origtext= for arg; do [[ $arg = -- ]] && break; origtext+="$arg "; done origtext=${origtext% } local styles=$*; [[ $styles = *' -- '* ]] || styles= read -a styles <<< "${styles##* -- }" [[ ${styles[0]} ]] && style=${styles[0]} [[ ${styles[1]} ]] && color=${styles[1]} [[ ${styles[2]} ]] && textstyle=${styles[2]} [[ ${styles[3]} ]] && textcolor=${styles[3]} # Figure out what FD to use for our messages. [[ -t 1 ]]; local fd=$(( $? + 1 )) # Make reset codes restore the initial font. local font=$reset$textstyle$textcolor origtext=$font${origtext//$reset/$font} (( noeol )) && text=$origtext || text=$origtext$reset$(eol "$origtext") # Spinner initial status. printf "\r$save$eel$reset $style$color* %s$reset" "$text" >&$fd (( output )) && printf "\n" >&$fd # Render the spinner. set +m local i=0 while [[ ! $done ]]; do IFS= read -r -d '' newtext || true newtext=${newtext%%$'\n'}; newtext=${newtext##*$'\n'} if [[ $newtext = +* ]]; then newtext="$origtext [${newtext#+}]" fi if [[ $newtext ]]; then newtext="$font${newtext//$reset/$font}" (( noeol )) && text=$newtext || text=$newtext$reset$(eol "$newtext") fi if (( output )) then printf "\r" >&$fd else printf "$load$eel" >&$fd fi if (( output )) then printf "$reset $style$color$blue%s %s$reset" \ "${graphics[i++ % 4]}" "$text" >&$fd else printf "$reset $style$color%s %s$reset" \ "${graphics[i++ % 4]}" "$text" >&$fd fi sleep .25 # Four iterations make one second. # Cancel when calling script disappears. kill -0 $$ >/dev/null || done="${red}aborted" done # Get rid of the spinner traps. trap - USR1 USR2; (( monitor )) && set -m # Spinner final status. if (( output )) then text=; printf "\r" >&$fd else printf "$load" >&$fd fi printf "$eel$reset $style$color* %s${text:+ }$bold%s$font.$reset\n" \ "$text" "$done" >&$fd } # _____________________________________________________________________ # ______________________________________________________________________ # |__ report ___________________________________________________________| # # report [-code] [-e] failure-message [success-message] # # This is a convenience function for replacement of spinner -code. # # It checks either the exit code of the previously completed command or # the code provided as option to determine whether to display the success # or failure message. It calls spinner -code to complete an actively # emitted message if there is one. The success message is optional. # # -[code] The exit code to use. # -e Exit the script on failure. # report() { # Exit Status of previous command. local code=$? # Parse the options. while [[ $1 = -* && $2 ]]; do arg=${1#-} shift # Stop parsing when arg is -- [[ $arg = - ]] && break # Process arg: Either a spinner type or result code. if [[ $arg = *[^0-9]* ]]; then case $arg in esac else code=$arg fi done # Initialize the vars. local failure=$1 local success=$2 # Check usage. (( ! $# )) || getArgs -q :h "$@" && { emit -y 'Please specify at least a failure message as argument.' return 1 } # Proxy call to spinner. (( spinPid )) \ && { spinner -$code; } # Success or failure message. if (( ! code )) then [[ $success ]] && emit " $success" else [[ $failure ]] && emit -R " $failure" fi # Pass on exit code. return $code } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Ask _______________________________________________________________| # # ask [-optionchars|+default] message... # # Ask a question and read the user's reply to it. # # By default, a reply is terminated by a newline. # # You may use the options to switch into key mode. In key mode, only a # single character is read. The valid characters are specified in the # optionchars. A capital option character makes that option the default. # # If the reply character in key mode was not amoungst the provided options # the default is assumed instead. If no default was given, an exit code # of 2 is returned. # # You may mark an optionchar as 'valid' by appending a '!' to it. As a # result, an exit code of 0 will only be returned if this valid option # is replied. If not, an exit code of 1 will be returned. # # If no option is marked as valid, the given reply is echoed and an exit # code of 0 is returned. # # You can specify the -# option to make ask hide the user's input. # # If you prefix the first argument with a + instead of a -, the remaining # argument is taken as the default string value and returned when no input # was received. In this case, the exit code is 0 either way. # ask() { # Check usage. (( ! $# )) || getArgs -q :h "$@" && { emit -y 'Please specify a question as argument.' return 1 } # Initialize the vars. local arg local option= local options= local default= local silent= local valid= local muteChar= # Parse the options. if [[ $1 = +* ]]; then option=${1#+} default=$option shift else for arg in $(getArgs "$(printf "%s" {a..z} {A..Z})!#%" "$@"); do [[ $arg = [[:upper:]] ]] \ && default=$arg [[ $arg = ! ]] \ && { valid=${options: -1}; continue; } [[ $arg = '#' ]] \ && { silent=1 arg=; } [[ $arg = '%' ]] \ && { silent=1 muteChar='*' arg=; } options+=$arg done fi # Trim off the options. while [[ $1 = -* ]]; do shift; done # Figure out what FD to use for our messages. [[ -t 1 ]]; local fd=$(( $? + 1 )) # Ask the question. emit -yn "$*${option:+ [$option]}${options:+ [$options]} " # Read the reply. if [[ $muteChar ]]; then local reply while read -s -n1 && [[ $REPLY ]]; do reply+=$REPLY printf "%s" "$muteChar" >&$fd done REPLY=$reply else read ${options:+-n1} ${silent:+-s} fi [[ $options && $REPLY ]] || (( silent )) && printf "\n" >&$fd # Evaluate the reply. while true; do if [[ $REPLY && ( ! $options || $options = *$REPLY* ) ]]; then if [[ $valid ]] then [[ $REPLY = $valid ]] else printf "%s" "$REPLY" fi return fi [[ -z $default || $REPLY = $default ]] \ && return 2 REPLY=$default done } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Trim ______________________________________________________________| # # trim lines ... # # Trim the whitespace off of the beginning and end of the given lines. # Each argument is considdered one line; is treated and printed out. # # When no arguments are given, lines will be read from standard input. # trim() { # Initialize the vars. local lines local line local oIFS # Get the lines. lines=( "$@" ) if (( ! ${#lines[@]} )); then oIFS=$IFS; IFS=$'\n' lines=( $(cat) ) IFS=$oIFS fi # Trim the lines for line in "${lines[@]}"; do line=${line##*([[:space:]])}; line=${line%%*([[:space:]])} printf "%s" "$line" done } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Reverse ___________________________________________________________| # # reverse [-0] [elements ...] [<<< elements] # # Reverse the order of the given elements. Elements are read from command # arguments or standard input if no element arguments are given. # They are reversed and output on standard output. # # If the -0 option is given, input and output are delimited by NUL bytes. # Otherwise, they are delimited by newlines. # reverse() { # Initialize the vars. local elements delimitor=$'\n' # Parse the options. while [[ $1 = -* ]]; do case $1 in -0) delimitor=$'\0' ;; --) shift; break ;; esac shift done # Get the elements. elements=( "$@" ) if (( ! ${#elements[@]} )); then while IFS= read -r -d "$delimitor"; do elements+=("$REPLY") done fi # Iterate in reverse order. for (( i=${#elements[@]} - 1; i >=0; --i )); do printf '%s%s' "${elements[i]}" "$delimitor" done } # _____________________________________________________________________ # ______________________________________________________________________ # |__ SWait _____________________________________________________________| # # swait pid... # # Wait for the given PID(s). The PID does not need to be a child of the # running shell. Note that relying on PIDs always introduces race conditions # which may be potentially harmful and sometimes even a security issue. # # This implementation requires the necessary permissions to send signals # to the PID(s) provided. # swait() { # Check usage. (( ! $# )) || getArgs -q :h "$@" && { emit -y 'Please provide one or more PIDs to wait for as argument.' return 1 } # Time to wait. local pid for pid; do while kill -0 $pid 2>/dev/null do sleep .1; done done } # _____________________________________________________________________ # ______________________________________________________________________ # |__ GetArgs ___________________________________________________________| # # getArgs [options] optstring [args...] # # Retrieve all options present in the given arguments. # # This is a wrapper for getopts(P) which will safely work inside functions. # It manages OPTIND for you and returns a list of options found in the # provided arguments. # # optstring This is a string of characters in which each character # represents an option to look for in the arguments. # See getopts(P) for a description of the optstring syntax. # # args This is a list of arguments in which to look for options. # Most commonly, you will use "$@" to supply these arguments. # # -c Instead of output the arguments, output OPTARGS. # -q Be quiet. No arguments are displayed. Only the exit code is set. # -n Use newlines as a separator between the options that were found. # -0 Use NULL-bytes as a separator between the options that were found. # # If any given arguments are found, an exit code of 0 is returned. If none # are found, an exit code of 1 is returned. # # After the operation, OPTARGS is set the the index of the last argument # that has been parsed by getArgs. Ready for you to use shift $OPTARGS. # getArgs() { # Check usage. (( ! $# )) && { emit -y 'Please provide the arguments to search for in' \ 'getopts(P) format followed by the positional parameters.' return 1 } # Initialize the defaults. local arg local found=0 local quiet=0 local count=0 local delimitor=' ' # Parse the options. while [[ $1 = -* ]]; do case $1 in -q) quiet=1 ;; -c) count=1 ;; -n) delimitor=$'\n' ;; -0) delimitor=$'\0' ;; esac shift done # Get the optstring. local optstring=$1; shift local oOPTIND=$OPTIND OPTIND=1 # Enumerate the arguments. while getopts "$optstring" arg; do [[ $arg != '?' ]] && found=1 (( quiet + count )) || \ printf "%s${OPTARG:+ }%s%s" "$arg" "$OPTARG" "$delimitor" done OPTARGS=$(( OPTIND - 1 )) OPTIND=$oOPTIND # Any arguments found? (( count )) && printf "%s" "$OPTARGS" return $(( ! found )) } # _____________________________________________________________________ # |__ ShowHelp __________________________________________________________| # # showHelp name description author [option description]... # # Generate a prettily formatted usage description of the application. # # name Provide the name of the application. # # description Provide a detailed description of the application's # purpose and usage. # # option An option the application can take as argument. # # description A description of the effect of the preceding option. # showHelp() { # Check usage. (( $# < 3 )) || getArgs -q :h "$@" && { emit -y 'Please provide the name, description, author and options' \ 'of the application.' return 1 } # Parse the options. local appName=$1; shift local appDesc=${1//+([[:space:]])/ }; shift local appAuthor=$1; shift local cols=$(tput cols) (( cols = ${cols:-80} - 10 )) # Figure out what FD to use for our messages. [[ -t 1 ]]; local fd=$(( $? + 1 )) # Print out the help header. printf "$reset$bold\n" >&$fd printf "\t\t%s\n" "$appName" >&$fd printf "$reset\n" >&$fd printf "%s\n" "$appDesc" | fmt -w "$cols" | sed $'s/^/\t/' >&$fd printf "\t $reset$bold~ $reset$bold%s\n" "$appAuthor" >&$fd printf "$reset\n" >&$fd # Print out the application options and columnize them. while (( $# )); do local optName=$1; shift local optDesc=$1; shift printf " %s\t" "$optName" printf "%s\n" "${optDesc//+( )/ }" | fmt -w "$cols" | sed $'1!s/^/ \t/' printf "\n" done | column -t -s $'\t' \ | sed "s/^\( [^ ]*\)/$bold$green\1$reset/" >&$fd printf "\n" >&$fd } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Quote _____________________________________________________________| # # quote [-e] [argument...] # # Output a single string where all arguments are quoted # such that the string is safe to be passed as shell # command arguments as though given arguments had been # passed. # # When no arguments are passed; no output is generated. # # -e Use backslashes rather than single quotes. # quote() { # Initialize the defaults. local arg escape=0 quotedArgs=() # Parse the options. while [[ $1 = -* ]]; do case $1 in -e) escape=1 ;; --) shift; break ;; esac shift done # Print out each argument, quoting it properly. for arg; do if (( escape )); then quotedArgs+=("$(printf "%q" "$arg")") else quotedArgs+=("$(printf "'%s'" "${arg//"'"/"\\'"}")") fi done printf '%s\n' "$(IFS=' '; echo "${quotedArgs[*]}")" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ Shorten ___________________________________________________________| # # shorten [-p pwd] path [suffix]... # # Shorten an absolute path for pretty printing by cutting # off PWD and replacing HOME by ~. # # -p Use the given pathname as the base for relative filenames instead of PWD. # path The path string to shorten. # suffix Suffix strings that must be cut off from the end. # Only the first suffix string matched will be cut off. # shorten() { # Check usage. (( $# < 1 )) || getArgs -q :h "$@" && { emit -y 'Please provide the path to shorten.' return 1 } # Parse the options. local suffix path pwd=$PWD [[ $1 = -p ]] && { pwd=$2; shift 2; } path=$1; shift # Make path absolute. [[ $path = /* ]] || path=$PWD/$path # If the path denotes something that exists; it's easy. if [[ -d $path ]] then path=$(cd "$path"; printf "%s" "$PWD") elif [[ -d ${path%/*} ]] then path=$(cd "${path%/*}"; printf "%s" "$PWD/${path##*/}") # If not, we'll try readlink -m. elif readlink -m / >/dev/null 2>&1; then path=$(readlink -m "$path") # If we don't have that - unleash the sed(1) madness. else local oldpath=/ while [[ $oldpath != $path ]]; do oldpath=$path path=$(sed -e 's,///*,/,g' -e 's,\(^\|/\)\./,\1,g' -e 's,\(^\|/\)[^/]*/\.\.\($\|/\),\1,g' <<< "$path") done fi # Replace special paths. path=${path#$pwd/} path=${path/#$HOME/'~'} # Cut off suffix. for suffix; do [[ $path = *$suffix ]] && { path=${path%$suffix} break } done printf "%s" "$path" } # _____________________________________________________________________ # ______________________________________________________________________ # |__ InArray ___________________________________________________________| # # inArray element array # # Checks whether a certain element is in the given array. # # element The element to search the array for. # array This is a list of elements to search through. # inArray() { # Check usage. (( $# < 1 )) || getArgs -q :h "$@" && { emit -y 'Please provide the element to search for and the array' \ 'to search through.' return 1 } # Parse the options. local element local search=$1; shift # Perform the search. for element do [[ $element = $search ]] && return 0; done return 1 } # _____________________________________________________________________ # ______________________________________________________________________ # |__ xpathNodes ________________________________________________________| # # xpathNodes query [filename] # # Outputs every xpath node that matches the query on a separate line. # Leading and trailing whitespace is always stripped. # # filename The path to the file that contains the document to run the query on. # query The XPath query to run on the document. # xpathNodes() { local query=$1 filename=$2 [[ $filename ]] || filename=<(cat) { if xpath -e / <(echo '') >/dev/null 2>&1; then xpath -e "$query" "$filename" 2>&1 else xpath "$filename" "$query" 2>&1 fi } | { read sed -ne $'s/-- NODE --/\\\n/g' -e 's/^[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$/\1/p' } return "${PIPESTATUS[0]}" } # ______________________________________________________________________ # |__ HideDebug _________________________________________________________| # # hideDebug [ on | off ] # # Toggle Bash's debugging mode off temporarily. To hide Bash's debugging # output for a function, you should have a hideDebug on as its first line # and hideDebug off as its last. # hideDebug() { if [[ $1 = on ]]; then : -- HIDING DEBUG OUTPUT .. [[ $- != *x* ]]; bashlib_debugWasOn=$? set +x elif [[ $1 = off ]]; then : -- SHOWING DEBUG OUTPUT .. (( bashlib_debugWasOn )) && \ set -x fi } # ______________________________________________________________________ # |__ StackTrace ________________________________________________________| # # stackTrace # # Retrieve a mapping of a key from the given map or modify the given map by # assigning a new value for the given key if stdin is not the terminal. # stackTrace() { # Some general debug information. printf "\t$bold%s$reset v$bold%s$reset" "$BASH" "$BASH_VERSION\n" printf " Was running: $bold%s %s$reset" "$BASH_COMMAND" "$*\n" printf "\n" printf " [Shell : $bold%15s$reset] [Subshells : $bold%5s$reset]\n" "$SHLVL" "$BASH_SUBSHELL" printf " [Locale : $bold%15s$reset] [Runtime : $bold%5s$reset]\n" "$LC_ALL" "${SECONDS}s" printf "\n" # Search through the map. local arg=0 for i in ${!FUNCNAME[@]}; do #if (( i )); then # Print this execution stack's location. printf "$reset $bold-$reset $green" [[ ${BASH_SOURCE[i+1]} ]] \ && printf "%s$reset:$green$bold%s" "${BASH_SOURCE[i+1]}" "${BASH_LINENO[i]}" \ || printf "${bold}Prompt" # Print this execution stack's function and positional parameters. printf "$reset :\t$bold%s(" "${FUNCNAME[i]}" [[ ${BASH_ARGC[i]} ]] && \ for (( j = 0; j < ${BASH_ARGC[i]}; j++ )); do (( j )) && printf ', ' printf "%s" "${BASH_ARGV[arg]}" let arg++ done # Print the end of this execution stack's line. printf ")$reset\n" #fi done printf "\n" } # _____________________________________________________________________ # ______________________________________________________________________ # | | # | .:: ENTRY POINT ::. | # |______________________________________________________________________| # Make sure this file is sourced and not executed. (( ! BASH_LINENO )) && { emit -R "You should source this file, not execute it." exit 1 } : : .:: END SOURCING ::. : ______________________________________________________________________ :