Skip to content

Instantly share code, notes, and snippets.

@jgornick
Last active March 22, 2023 10:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jgornick/528ab6a181204f0d5b02 to your computer and use it in GitHub Desktop.
Save jgornick/528ab6a181204f0d5b02 to your computer and use it in GitHub Desktop.
Bash: Helper Functions
#!/bin/bash
# Returns a UTC timestamp of the current date/time.
# Can provide an optional format that matches date(1) format
now() {
local format="${1:-"%s"}"
echo -n "$(date -u +"$format")"
}
# Extract the first element of a list.
head() {
echo -n "${@:1:1}"
}
# Extract the last element of a list.
last() {
echo -n "${@:(-1)}"
}
# Return all the elements of a list except the last one.
init() {
echo -n "${@:1:(($#-1))}"
}
# Extract the elements after the head of a list.
tail() {
echo -n "${@:2}"
}
# Retry a function
# @param $1 number Retry Times
# @param $2 number Retry Wait
# @param $@ mixed Function and arguments
retry() {
times=$1; shift;
wait=$1; shift;
until "$@"; do
(( --times <= 0 )) && return 1
sleep $wait
done
}
# Negate the provided function return code.
not() {
! "$@"
}
# Hide all stdout and stderr from the provided function
quiet() {
"$@" >& /dev/null
}
# Creates a list (newline) from the specified arguments
list() {
printf %s\\n "$@"
}
# Creates a list (no-newline) from the specified arguments
unlist() {
echo -n "$@"
}
# Composes the two specified functions and applies any arguments to the second.
# @param name The name of the new composed function
# @param f1 The first (outer) function
# @param f2 The second (inner) function
compose() {
name=$1; shift
f1=$1; shift
f2=$1; shift
eval "$name() { $f1 \$($f2 \$*); }"
echo -n $name
}
# Creates a new function with the arguments applied to the specified function.
# @param name The name of the new function
# @param func The function to partially apply arguments
# @param args The arguments to apply to the function
partial() {
local name=$1; shift
local func=$1; shift
local args=$*
eval "$name() { $func $args \$*; }"
echo -n $name
}
map() {
local collection=( $(init "$@") )
local iteratee=$(last "$@")
local result=()
local value
local ret
for i in "${collection[@]}"; do
value="$($iteratee "$i")"
ret=$?
if (( ret != 0 )); then
echo -n "$value"
return $ret
fi
result+=( "$value" )
done
list "${result[@]}"
}
isInt() {
[[ $1 =~ ^-?[0-9]+$ ]]
}
indexOf() {
local collection=( $(init "$@") )
local value=$(last "$@")
for i in "${!collection[@]}"; do
if [[ "${collection[$i]}" == "${value}" ]]; then
echo -n "$i"
return 0
fi
done
echo -n "-1"
return 1
}
min() {
local result="$1"
for i in "$@"; do
result=$(( i < result ? i : result ))
done
echo -n "$result"
}
max() {
local result="$1"
for i in "$@"; do
result=$(( i > result ? i : result ))
done
echo -n "$result"
}
extractNumber() {
echo -n "$(echo "$1" | sed 's/[^0-9]//g')"
}
extractString() {
echo -n "$(echo "$1" | sed 's/[0-9]//g')"
}
# Similar to timeout(1) except that this will call a provided iteratee argument if specified
# for each iteration to check the command
# @param mixed cmd Function and arguments
# @param number duration Timeout duration in seconds
# @param function [iteratee] Optional function to call each check iteration
timeout() (
local args=( "$@" )
local cmd
local iteratee
local duration=$(last "${args[@]}")
if ! isInt "$duration"; then
# The last argument is a function (iteratee)
iteratee="$duration"
# Pop the iteratee off the args
args=( $(init "${args[@]}") )
# Set the duration to the last of the args
duration=$(last "${args[@]}")
fi
if ! (( duration > 0 )); then
echo "Missing duration argument"
return 1
fi
# Pop the duration off the args
args=( $(init "${args[@]}") )
cmd=( "${args[@]}" )
"${cmd[@]}" &
local cmdPid=$!
local start="$(now)"
while (( $(now) - start < duration )); do
if kill -0 "$cmdPid" >& /dev/null; then
[[ $iteratee ]] && $iteratee "$cmdPid"
else
wait "$cmdPid" >& /dev/null
return $?
fi
sleep 1
done
if ! kill "$cmdPid" >& /dev/null; then
kill -9 "$cmdPid" >& /dev/null
return 9
fi
return 1
)
lowerCase() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/./\L&/g'
}
lowerFirst() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/([a-zA-Z])(.*)/\L\1\E\2/g'
}
upperCase() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/./\U&/g'
}
upperFirst() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/([a-zA-Z])(.*)/\U\1\E\2/g'
}
capitalize() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/([a-zA-Z])(.*)/\U\1\L\2/g'
}
camelCase() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/[-_ ]([a-zA-Z0-9])/\u\1/g' \
| sed -re 's/([a-zA-Z])(.*)/\L\1\E\2/g'
}
kebabCase() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/[_ ]+/-/g' \
| sed -re 's/([A-Z])/-\1/g' \
| sed -re 's/-+/-/g' \
| sed -re 's/./\L&/g'
}
snakeCase() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/[- ]+/_/g' \
| sed -re 's/([A-Z])/_\1/g' \
| sed -re 's/_+/_/g' \
| sed -re 's/./\L&/g'
}
titleCase() {
if (( $# )); then printf %s\\n "$*" | "${FUNCNAME[0]}"; return; fi
sed -re 's/[-_]+/ /g' \
| sed -re 's/^[ ]+//' \
| sed -re 's/[ ]+$//' \
| sed -re 's/.*/\L&/' \
| sed -re 's/[[:graph:]]*/\u&/g'
}
array-includes () {
local array=($(init "$@"))
local target=$(last "$@")
for e in "${array[@]}"; do
[[ $e == $target ]] && return 0
done
return 1
}
run-command-on-ip () {
local ip="${1:?Missing IP address}"; shift
ssh \
-o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-o BatchMode=yes \
-o ConnectTimeout=1 \
-o ConnectionAttempts=1 \
-q \
"$ip" \
"$@"
}
run-script-on-ip () {
local ip="${1:?Missing IP address}"; shift
local script="${1:?Missing script}"; shift
ssh \
-o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-o BatchMode=yes \
-o ConnectTimeout=1 \
-o ConnectionAttempts=1 \
-q \
"$ip" \
"sudo bash -s" < "$script"
}
copy-file-to-ip () {
local target="${1:?"Missing target address:path"}"
local file="${2:?Missing file}"
scp \
-o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-o BatchMode=yes \
-o ConnectTimeout=1 \
-o ConnectionAttempts=1 \
-q \
"$file" "$target"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment