Added InternetArchive/function_lib.sh
InternetArchive/function_lib.sh: new file; subset of '~/bin/function_lib.sh' which is referred to in a number of scripts. It contains relevant functions such as 'yes_no' and 'define_colours'.
This commit is contained in:
parent
6805cd662b
commit
9203dc26e0
477
InternetArchive/function_lib.sh
Normal file
477
InternetArchive/function_lib.sh
Normal file
@ -0,0 +1,477 @@
|
||||
#
|
||||
# Bash Functions for sourcing in scripts.
|
||||
#
|
||||
# This is a copy of some of the functions for HPR project use.
|
||||
#
|
||||
# ${HOME}/HPR/InternetArchive/function_lib.sh
|
||||
#
|
||||
# Revised: 2024-06-15 18:13:28
|
||||
#
|
||||
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: abs_diff
|
||||
# DESCRIPTION: Compute the difference between two numbers, returning an
|
||||
# absolute value
|
||||
# PARAMETERS: $1 first number
|
||||
# $2 second number
|
||||
# RETURNS: The absolute value as a string
|
||||
#===============================================================================
|
||||
function abs_diff {
|
||||
local p1="${1:?Usage: abs_diff n1 n2}"
|
||||
local p2="${2:?Usage: abs_diff n1 n2}"
|
||||
|
||||
echo $((p1 >= p2 ? p1 - p2 : p2 - p1))
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: cleanup_temp
|
||||
# DESCRIPTION: Cleanup temporary files in case of a keyboard interrupt
|
||||
# (SIGINT) or a termination signal (SIGTERM) and at script
|
||||
# exit
|
||||
# PARAMETERS: * - names of temporary files to delete
|
||||
# RETURNS: Nothing
|
||||
#===============================================================================
|
||||
function cleanup_temp {
|
||||
for tmp in "$@"; do
|
||||
[ -e "$tmp" ] && rm --force "$tmp"
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: _DEBUG
|
||||
# DESCRIPTION: Writes one or more messages if in DEBUG mode. Each argument is
|
||||
# seen as a message and is written on a separate line.
|
||||
# References the global variable 'DEBUG' which is expected to be
|
||||
# True if debug output is wanted.
|
||||
# PARAMETERS: List of messages
|
||||
# RETURNS: Nothing
|
||||
#===============================================================================
|
||||
_DEBUG () {
|
||||
[ "$DEBUG" == 0 ] && return
|
||||
for msg in "$@"; do
|
||||
printf 'D> %s\n' "$msg"
|
||||
done
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: yes_no
|
||||
# DESCRIPTION: Read a Yes or No response from STDIN (only these values are
|
||||
# accepted) and return a suitable numeric value.
|
||||
# PARAMETERS: 1 - Prompt string for the read
|
||||
# 2 - Default value (optional)
|
||||
# RETURNS: 0 for a response of Y or YES, 1 otherwise
|
||||
#===============================================================================
|
||||
yes_no () {
|
||||
local prompt="${1:?Usage: yes_no prompt [default]}"
|
||||
local default="${2^^}"
|
||||
local ans res
|
||||
|
||||
if [[ $prompt =~ %s ]]; then
|
||||
if [[ -n $default ]]; then
|
||||
default=${default:0:1}
|
||||
# shellcheck disable=SC2059
|
||||
# {
|
||||
case "$default" in
|
||||
Y) printf -v prompt "$prompt" "[Y/n]" ;;
|
||||
N) printf -v prompt "$prompt" "[y/N]" ;;
|
||||
*) echo "Error: ${FUNCNAME[0]} @ line ${BASH_LINENO[0]}: Default must be 'Y' or 'N'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
# }
|
||||
else
|
||||
echo "Error: ${FUNCNAME[0]} @ line ${BASH_LINENO[0]}: Default required"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Loop until a valid input is received
|
||||
#
|
||||
while true; do
|
||||
#
|
||||
# Read and handle CTRL-D (EOF)
|
||||
#
|
||||
read -r -e -p "$prompt" ans
|
||||
res="$?"
|
||||
if [[ $res -ne 0 ]]; then
|
||||
echo "Read aborted"
|
||||
return 1
|
||||
fi
|
||||
|
||||
[ -z "$ans" ] && ans="$default"
|
||||
|
||||
#
|
||||
# Look for valid replies and return appropriate values. Print an error
|
||||
# message otherwise and loop around for another go
|
||||
#
|
||||
if [[ ${ans^^} =~ ^Y(E|ES)?$ ]]; then
|
||||
return 0
|
||||
elif [[ ${ans^^} =~ ^NO?$ ]]; then
|
||||
return 1
|
||||
else
|
||||
echo "Invalid reply; please use 'Y' or 'N'"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: make_file_list
|
||||
# DESCRIPTION: Given an array of filenames select from them and make
|
||||
# an array of choices in a different order if required
|
||||
# PARAMETERS: 1 - input array name
|
||||
# 2 - output array name
|
||||
# RETURNS: True (0) if a new list was generated, false (1) otherwise
|
||||
#===============================================================================
|
||||
make_file_list () {
|
||||
local -n inarr=${1:?Usage make_file_list input_array out_array}
|
||||
local -n outarr=${2:?Usage make_file_list input_array out_array}
|
||||
local i msg prompt range lines choices
|
||||
|
||||
#
|
||||
# Print the array contents
|
||||
#
|
||||
lines="${#inarr[@]}"
|
||||
for ((i = 0; i < lines; i++)); do
|
||||
printf '%-2d %s\n' "$((i+1))" "${inarr[$i]}"
|
||||
done
|
||||
|
||||
msg="Enter a range like '1,2' or '4-7'"
|
||||
if [[ $lines -eq 1 ]]; then
|
||||
prompt="1"
|
||||
else
|
||||
prompt="1-$lines"
|
||||
msg+=" (between 1 and $lines)"
|
||||
fi
|
||||
|
||||
#
|
||||
# Choose array elements by number where order is significant
|
||||
#
|
||||
echo "$msg"
|
||||
if read_value "Files to add to list: " range "$prompt"; then
|
||||
#
|
||||
# If there's a range then parse it and write the relevant elements to the
|
||||
# target array, otherwise exit with an error
|
||||
#
|
||||
if [[ -n "$range" ]]; then
|
||||
range_parse "$lines" "$range" choices 0
|
||||
for i in $choices; do
|
||||
outarr+=("${inarr[$((i-1))]}")
|
||||
done
|
||||
else
|
||||
echo "No files chosen; no list created"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: range_parse
|
||||
# DESCRIPTION: Parse a collection of ranges
|
||||
# PARAMETERS: - maximum limit of the range
|
||||
# - entered range expression (e.g. 1-3,7,14)
|
||||
# - name of variable to receive the result
|
||||
# RETURNS: Writes a list of values to the nominated variable
|
||||
#===============================================================================
|
||||
function range_parse {
|
||||
local max=${1?range_parse: arg 1 missing}
|
||||
local range=${2?range:parse: arg2 missing}
|
||||
local result=${3?range:parse: arg3 missing}
|
||||
local tosort=${4:-1}
|
||||
|
||||
local selection=
|
||||
|
||||
#
|
||||
# Turn this off. It affects how some code below behaves
|
||||
#
|
||||
set +o nounset
|
||||
|
||||
#
|
||||
# Remove spaces from the range
|
||||
#
|
||||
range=${range// /}
|
||||
|
||||
#
|
||||
# Check for invalid characters
|
||||
#
|
||||
if [[ $range =~ [^0-9,-] ]]; then
|
||||
echo "Invalid range: $range"
|
||||
exit
|
||||
fi
|
||||
|
||||
#
|
||||
# Slice up the sub-ranges separated by commas and turn all n-m expressions
|
||||
# into the intermediate values. Trim the trailing space from the
|
||||
# concatenation.
|
||||
#
|
||||
until [[ -z $range ]]; do
|
||||
if [[ $range =~ [,] ]]; then
|
||||
item=${range%%,*}
|
||||
range=${range#*,}
|
||||
else
|
||||
item=$range
|
||||
range=
|
||||
fi
|
||||
if [[ $item =~ [-] ]]; then
|
||||
item=${item//-/ }
|
||||
if [[ $item =~ ^([0-9]{1,}).([0-9]{1,})$ ]]; then
|
||||
# shellcheck disable=SC2086
|
||||
item=$(seq -s' ' $item)
|
||||
else
|
||||
echo "Invalid sequence: ${item/ /-}"
|
||||
item=
|
||||
fi
|
||||
fi
|
||||
selection+="$item "
|
||||
done
|
||||
selection=${selection%% }
|
||||
|
||||
#
|
||||
# Check for out of bounds problems, sort values and and make unique
|
||||
#
|
||||
if [[ ${#selection} -gt 0 ]]; then
|
||||
#
|
||||
# Validate the resulting range
|
||||
#
|
||||
declare -a sel err msg
|
||||
for i in $selection; do
|
||||
if [[ $i -lt 1 || $i -gt $max ]]; then
|
||||
err+=( $i )
|
||||
else
|
||||
sel+=( $i )
|
||||
fi
|
||||
done
|
||||
|
||||
#
|
||||
# Report any errors
|
||||
#
|
||||
if [[ ${#err[*]} -gt 0 ]]; then
|
||||
msg=( $(for i in ${err[*]}; do echo $i; done | sort -un) )
|
||||
if [[ ${#msg[*]} -gt 1 ]]; then
|
||||
printf "Values out of range: %s .. %s\n" ${msg[0]} ${msg[${#msg[*]}-1]}
|
||||
else
|
||||
printf "Value out of range: %s\n" ${msg[0]}
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Rebuild the selection
|
||||
#
|
||||
selection=
|
||||
if [[ ${#sel[*]} -gt 0 ]]; then
|
||||
if [[ $tosort -eq 1 ]]; then
|
||||
selection="$(for i in ${sel[*]}; do echo $i; done | sort -un)"
|
||||
else
|
||||
selection="$(for i in ${sel[*]}; do echo $i; done)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Return the result
|
||||
#
|
||||
eval $result=\"$selection\"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: pad
|
||||
# DESCRIPTION: Pad $text to the left with $char characters to length $length
|
||||
# PARAMETERS: 1 - the text string to pad (no default)
|
||||
# 2 - how long the padded string is to be (default 80)
|
||||
# 3 - the character to pad with (default '-')
|
||||
# RETURNS: Nothing
|
||||
#===============================================================================
|
||||
pad () {
|
||||
local text=${1?Usage: pad text [length] [character]}
|
||||
local length=${2:-80}
|
||||
local char=${3:--}
|
||||
|
||||
local line
|
||||
printf -v line "%0*d%s\n" $((length - ${#text})) 0 "$text"
|
||||
char=${char:0:1}
|
||||
echo "${line//0/$char}"
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: read_value
|
||||
# DESCRIPTION: Read a value from STDIN and handle errors.
|
||||
# PARAMETERS: 1 - Prompt string for the read
|
||||
# 2 - Name of variable to receive the result
|
||||
# 3 - Default value (optional)
|
||||
# RETURNS: 1 on error, otherwise 0
|
||||
#===============================================================================
|
||||
read_value () {
|
||||
local prompt="${1:?Usage: read_value prompt outputname [default]}"
|
||||
local outputname="${2:?Usage: read_value prompt outputname [default]}"
|
||||
local default="${3:-}"
|
||||
local var
|
||||
|
||||
#
|
||||
# Make an option for the 'read' if there's a default
|
||||
#
|
||||
if [[ -n $default ]]; then
|
||||
default="-i '$default'"
|
||||
fi
|
||||
|
||||
#
|
||||
# Read and handle CTRL-D (EOF). Use 'eval' to deal with the argument being
|
||||
# a variable
|
||||
#
|
||||
eval "read -r -e $default -p '$prompt' var"
|
||||
res="$?"
|
||||
if [[ $res -ne 0 ]]; then
|
||||
echo "Read aborted"
|
||||
return 1
|
||||
fi
|
||||
|
||||
#
|
||||
# Return the value in the nominated variable
|
||||
#
|
||||
eval "$outputname='$var'"
|
||||
return 0
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: define_colours
|
||||
# DESCRIPTION: Defines terminal colours
|
||||
# PARAMETERS: None
|
||||
# RETURNS: Nothing; justs sets the variables in the namespace
|
||||
#===============================================================================
|
||||
define_colours () {
|
||||
|
||||
#
|
||||
# Colour codes
|
||||
#
|
||||
red=$(tput setaf 1)
|
||||
export red
|
||||
green=$(tput setaf 2)
|
||||
export green
|
||||
yellow=$(tput setaf 3)
|
||||
export yellow
|
||||
blue=$(tput setaf 4)
|
||||
export blue
|
||||
purple=$(tput setaf 5)
|
||||
export purple
|
||||
reset=$(tput sgr0)
|
||||
export reset
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: undefine_colours
|
||||
# DESCRIPTION: Remove all the colour codes from the colour variables
|
||||
# PARAMETERS: None
|
||||
# RETURNS: Nothing; justs sets the variables in the namespace
|
||||
#===============================================================================
|
||||
undefine_colours () {
|
||||
|
||||
#
|
||||
# Colour codes being removed
|
||||
#
|
||||
for code in red green yellow blue purple reset; do
|
||||
eval "$code=''"
|
||||
eval "export $code"
|
||||
done
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: coloured
|
||||
# DESCRIPTION: Write a message with colour codes
|
||||
# PARAMETERS: 1 - colour name
|
||||
# 2 - message
|
||||
# RETURNS: Nothing
|
||||
#===============================================================================
|
||||
coloured () {
|
||||
local colour="${1:-green}"
|
||||
local message="${2:-no message}"
|
||||
|
||||
printf '%s%s%s\n' "${!colour}" "$message" "${reset}"
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: plural
|
||||
# DESCRIPTION: Adds an 's' to a word depending on the size of a number (based
|
||||
# on an example from O'Reilly). Also look at ngettext for a much
|
||||
# more sophisticated alternative.
|
||||
# PARAMETERS: 1 - the word to pluralise
|
||||
# 2 - the number used to decide
|
||||
# RETURNS: Nothing
|
||||
#===============================================================================
|
||||
plural () {
|
||||
if [[ $2 -eq 1 || $2 -eq -1 ]]; then
|
||||
echo "${1}"
|
||||
else
|
||||
echo "${1}s"
|
||||
fi
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: trimpath
|
||||
# DESCRIPTION: Trim a file path to its last few components for display
|
||||
# PARAMETERS: $1 - path to trim
|
||||
# $2 - number of elements to retain
|
||||
# RETURNS: Trimmed path
|
||||
#===============================================================================
|
||||
trimpath () {
|
||||
local path=${1:?Usage: mpath path elements}
|
||||
local elements=${2:?Usage: mpath path elements}
|
||||
local -a arr1 arr2
|
||||
local result
|
||||
|
||||
# Split the path
|
||||
# oldifs="$IFS"
|
||||
# IFS='/' mapfile -d'/' -t arr1 <<<"$path"
|
||||
IFS='/' read -r -a arr1 <<<"$path"
|
||||
# IFS="$oldifs"
|
||||
|
||||
if [[ ${#arr1[@]} -gt $elements ]]; then
|
||||
# Put the last elements in another array
|
||||
mapfile -t arr2 < <(printf '%s\n' "${arr1[@]: -$elements}")
|
||||
|
||||
# Return the second array interleaved with '/'
|
||||
result="${arr2[*]/%//}"
|
||||
result="${result//\/ /\/}"
|
||||
echo "${result:0:-1}"
|
||||
else
|
||||
echo "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# NAME: stampfile
|
||||
# DESCRIPTION: Rename a file by adding a timestamp to its name. The timestamp
|
||||
# is the modification time. It is added at the end by default or
|
||||
# wherever the caller places a '%s'. Note there are not enough
|
||||
# error checks to deal with really weird requests :-)
|
||||
# PARAMETERS: $1 - name of file to rename
|
||||
# $2 - optional template string
|
||||
# RETURNS: Nothing
|
||||
#===============================================================================
|
||||
function stampfile () {
|
||||
if [[ $# = 0 || $# -gt 2 ]]; then
|
||||
echo "Usage: ${FUNCNAME[0]} filename [template]"
|
||||
return
|
||||
fi
|
||||
local fname=${1}
|
||||
local tp=${2:-"${fname}_%s"}
|
||||
[ -e "$fname" ] || { echo "$fname does not exist"; return; }
|
||||
# shellcheck disable=SC2059
|
||||
new=$(printf "$tp" "$(date -r "$fname" +%Y%m%d_%H%M%S)")
|
||||
mv "$fname" "$new" || { echo "Unable to mv $fname to $new"; return; }
|
||||
echo "Renamed $fname to $new"
|
||||
}
|
||||
|
||||
#
|
||||
# vim: syntax=sh:ts=8:sw=4:et:tw=78:fo=tcrqn21
|
Loading…
Reference in New Issue
Block a user