Skip to content

Instantly share code, notes, and snippets.

@tie
Last active August 6, 2024 22:24
Show Gist options
  • Save tie/7e81aaa4b357c486abc197bdbe00f27c to your computer and use it in GitHub Desktop.
Save tie/7e81aaa4b357c486abc197bdbe00f27c to your computer and use it in GitHub Desktop.
# This file is a library of Bash functions useful in practice for writing
# reliable scripts.
#
# References:
# • https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
# These function accept variable names instead of their values to make misuse
# harder. In particular, $(…) expressions do not fail the script even if errexit
# is set. Instead of `printf '{"string":"%s"}' "$(escapeJSONString "$v")", write
# `escapeJSONString v && printf '{"string":"%s"}\n' "$v"` (or equivalent).
# Escape a string for use in JSON templates.
# See https://json.org
#
# Assumes that the current locale is compatible with Unicode.
#
# Example:
# $ bar='"hello world"
# \(^-^)/'
# $ escapeJSONString bar
# $ printf '{"text":"%s"}\n' "$bar"
function escapeJSONString() {
# TODO: implement "${!varName@J}" in Bash?
local varName=$1
local in=${!varName} out=
local char
local -i charCode
while [[ -n $in ]]; do
char=${in:0:1}
in=${in:1}
case "$char" in
$'\b') out+='\b' ;;
$'\f') out+='\f' ;;
$'\n') out+='\n' ;;
$'\r') out+='\r' ;;
$'\t') out+='\t' ;;
\") out+='\"' ;;
\\) out+='\\' ;;
*)
printf -v charCode %d "'$char"
# Escape control characters as 4 hex digits.
if ((charCode < 0x20)); then
printf -v char \\u00%x $charCode
fi
out+=$char
;;
esac
done
printf -v "$varName" "%s" "$out"
}
# Prefix a value of the given variable name (first argument, required) if it
# starts with a string (second argument, defaults to “-”) with the third
# argument (defaults to “./”).
#
# Intended use case is to prefix paths for commands that do not implement a
# common convention of separating options and further arguments with "--". For
# example, `find`, `cc` and `echo`.
#
# Example:
# $ foo=-n
# $ prefixPath foo
# $ echo "$foo"
# ./-n
function prefixPath() {
local varName=$1
local pattern=${2--}
local prefix=${3-./}
printf -v "$varName" "%s" "${!varName/#$pattern/"$prefix$pattern"}"
}
# Escape _replacement_ string for sed’s `s` command.
# See https://www.gnu.org/software/sed/manual/html_node/The-_0022s_0022-Command.html
#
# Fails if patsub_replacement shell option is not set.
#
# Usage:
# $ escapeSedReplacement foo :
# $ sed "s:foo:$foo"
#
# Example:
# $ bar='/usr/bin/\&bar'
# $ quoteSedReplacement bar
# $ echo "$bar"
# \/usr\/bin\/\\\&bar
function escapeSedReplacement() {
local varName=$1
local sep=${2-/}
shopt -q patsub_replacement
printf -v "$varName" "%s" "${!varName//[$sep\\&$'\n']/\\&}"
}
# Checks whether file at the given path has executable file mode bit set.
#
# Example:
# $ isExecutableFile /bin/sh
function isExecutableFile() {
local fn=$1
[[ -f $fn && -x $fn ]]
}
# Checks whether the given variable name is an indexed array.
#
# Example:
# $ declare -A var=(foo bar)
# $ isIndexedArray var
function isIndexedArray() {
local varName=$1
[[ ${!varName@a} == *a* ]]
}
# Checks whether the given variable name is an associative array.
#
# Example:
# $ declare -A var=([foo]=bar [baz]=quux)
# $ isAssociativeArray var
function isAssociativeArray() {
local varName=$1
[[ ${!varName@a} == *A* ]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment