Initial commit

This commit is contained in:
labbots
2020-06-06 21:05:52 +01:00
parent c1f82b0815
commit 320f29d373
10 changed files with 818 additions and 0 deletions

22
.editorconfig Normal file
View File

@@ -0,0 +1,22 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# for shfmt
[*.sh]
indent_style = space
indent_size = 4
shell_variant = bash
switch_case_indent = true
space_redirects = true
[*.md]
trim_trailing_whitespace = false

7
.remarkrc Normal file
View File

@@ -0,0 +1,7 @@
{
"plugins": [
"remark-preset-lint-markdown-style-guide",
["remark-lint-list-item-spacing", false],
["remark-lint-maximum-line-length", false]
]
}

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <admin@labbots.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>
For answers to common questions about this code of conduct, see
<https://www.contributor-covenant.org/faq>
[homepage]: https://www.contributor-covenant.org

4
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,4 @@
# Contributing to Bash-Utility
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
The following is a set of guidelines for contributing to this project on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.

4
bash_utilities.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
source src/array.sh
source src/string.sh

156
bin/generate_readme.sh Executable file
View File

@@ -0,0 +1,156 @@
#!/usr/bin/env bash
README="README.md"
_setup() {
INVALID_CHARS="'[]/?!:\`.,()*\";{}+=<>~$|#@&–—"
MINLEVEL=1
MAXLEVEL=2
SCRIPT_FILE=$(basename $0)
SCRIPT_FILENAME="${SCRIPT_FILE%.*}"
rm README.md
cp readme-template.md "$README"
}
_setup_tempfile() {
declare temp_file
type -p mktemp &> /dev/null && { temp_file="$(mktemp -u)" || temp_file="${PWD}/$((RANDOM * 2)).LOG"; }
trap 'rm -f "${temp_file}"' EXIT
printf "%s" "${temp_file}"
}
_generate_shdoc() {
declare file
file="$(realpath $1)"
./shdoc.awk < $file >> "$2"
}
_insert_shdoc_to_file() {
declare shdoc_tmp_file="$1"
declare dest_file="$2"
declare start_shdoc="<!-- START ${SCRIPT_FILE} generated SHDOC please keep comment here to allow auto update -->"
declare info_shdoc="<!-- DO NOT EDIT THIS SECTION, INSTEAD RE-RUN ${SCRIPT_FILE} TO UPDATE -->"
declare end_shdoc="<!-- END ${SCRIPT_FILE} generated SHDOC please keep comment here to allow auto update -->"
sed -i "1s/^/$info_shdoc\n/" "$shdoc_temp_file"
if grep --color=always -Pzl "(?s)$start_shdoc.*\n.*$end_shdoc" $dest_file &> /dev/null; then
# src https://stackoverflow.com/questions/2699666/replace-delimited-block-of-text-in-file-with-the-contents-of-another-file
sed -i -ne "/$start_shdoc/ {p; r $shdoc_temp_file" -e ":a; n; /$end_shdoc/ {p; b}; ba}; p" $dest_file
echo -e "\n Updated shdoc content to $dest_file\n"
else
echo -e "\n Created\n"
fi
}
_process_sh_files() {
declare shdoc_temp_file=$(_setup_tempfile)
find ../src -name '*.sh' -print0 | sort -z |
while IFS= read -r -d '' line; do
_generate_shdoc "$line" "$shdoc_temp_file"
done
_insert_shdoc_to_file "$shdoc_temp_file" "$README"
rm "$shdoc_temp_file"
}
_generate_toc() {
declare line level title anchor output counter temp_output
while IFS='' read -r line || [[ -n "$line" ]]; do
level="$(echo "$line" | sed -E 's/(#+).*/\1/; s/#/ /g; s/^ //')"
title="$(echo "$line" | sed -E 's/^#+ //')"
# tr does not do OK the lowercase for non ascii chars, add sed to pipeline -> src https://stackoverflow.com/questions/13381746/tr-upper-lower-with-cyrillic-text
anchor="$(echo "$title" | tr '[:upper:] ' '[:lower:]-' | sed 's/[[:upper:]]*/\L&/' | tr -d "$INVALID_CHARS")"
# check new line introduced is not duplicated, if is duplicated, introduce a number at the end
# copying doctoc behavior
temp_output=$output"$level- [$title](#$anchor)\n"
counter=1
while true; do
nlines="$(echo -e $temp_output | wc -l)"
duplines="$(echo -e $temp_output | sort | uniq | wc -l)"
if [ $nlines = $duplines ]; then
break
fi
temp_output=$output"$level- [$title](#$anchor-$counter)\n"
counter=$(($counter + 1))
done
output="$temp_output"
# grep: filter header candidates to be included in toc
# sed: remove the ignored headers (case: minlevel greater than one) to avoid unnecessary spacing later in level variable assignment
done <<< "$(grep -E "^#{${MINLEVEL},${MAXLEVEL}} " "$1" | tr -d '\r' | sed "s/^#\{$(($MINLEVEL - 1))\}//g")"
# when in toc we have two `--` quit one
output="$(echo "$output" | sed 's/--*/-/g')"
echo "$output"
}
_insert_toc_to_file() {
declare toc_text="$2"
# inspired in doctoc lines
declare start_toc="<!-- START ${SCRIPT_FILE} generated TOC please keep comment here to allow auto update -->"
declare info_toc="<!-- DO NOT EDIT THIS SECTION, INSTEAD RE-RUN ${SCRIPT_FILE} TO UPDATE -->"
declare end_toc="<!-- END ${SCRIPT_FILE} generated TOC please keep comment here to allow auto update -->"
toc_block="$start_toc\n$info_toc\n**Table of Contents**\n\n$toc_text\n$end_toc"
# temporary replace of '/' (confused with separator of substitutions) and '&' (confused with match regex symbol) to run the special sed command
utext_ampersand="id8234923000230gzz"
utext_slash="id9992384923423gzz"
toc_block="$(echo "$toc_block" | sed "s,\&,$utext_ampersand,g")"
toc_block="$(echo "$toc_block" | sed "s,\/,$utext_slash,g")"
# search multiline toc block -> https://stackoverflow.com/questions/2686147/how-to-find-patterns-across-multiple-lines-using-grep/2686705
# grep color for debugging -> https://superuser.com/questions/914856/grep-display-all-output-but-highlight-search-matches
if grep --color=always -Pzl "(?s)$start_toc.*\n.*$end_toc" $1 &> /dev/null; then
echo -e "\n Updated content of $appname block in $1 succesfully\n"
# src https://askubuntu.com/questions/533221/how-do-i-replace-multiple-lines-with-single-word-in-fileinplace-replace
sed -i ":a;N;\$!ba;s/$start_toc.*$end_toc/$toc_block/g" $1
else
echo -e "\n Created $appname block in $1 succesfully\n"
sed -i 1i"$toc_block" "$1"
fi
# undo symbol replacements
sed -i "s,$utext_ampersand,\&,g" $1
sed -i "s,$utext_slash,\/,g" $1
}
_process_toc() {
declare toc_temp_file=$(_setup_tempfile)
sed '/```/,/```/d' "$README" > "$toc_temp_file"
declare level=$MINLEVEL
while [[ $(grep -E "^#{$level} " "$toc_temp_file" | wc -l) -le 1 ]]; do
level=$(($level + 1))
done
if [[ $MINLEVEL -ne $level ]]; then
echo -e "\nnote: detected all headers (maybe except 1) in level $level, switching to that level of headers to fill table of contents"
fi
MINLEVEL=$level
toc_text=$(_generate_toc "$toc_temp_file")
rm "$toc_temp_file"
_insert_toc_to_file "$README" "$toc_text"
}
main() {
_setup
_process_sh_files
_process_toc
}
main "$@"

21
bin/readme-template.md Normal file
View File

@@ -0,0 +1,21 @@
<h1 align="center">Bash Utilites</h1>
<p align="center">
<a href="https://github.com/labbots/bash-utility/blob/master/LICENSE"><img src="https://img.shields.io/github/license/labbots/bash-utility.svg?style=for-the-badge" alt="License"></a>
<a href="https://www.codacy.com/manual/labbots/bash-utility?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=labbots/bash-utility&amp;utm_campaign=Badge_Grade"><img src="https://img.shields.io/codacy/grade/99fbe8d389254b6ebb37899ce89658e3?style=for-the-badge"/></a>
</p>
Bash library which provides utility functions and helpers for functional programming in Bash.
<!-- START generate_readme.sh generated TOC please keep comment here to allow auto update -->
<!-- DO NOT EDIT THIS SECTION, INSTEAD RE-RUN generate_readme.sh TO UPDATE -->
<!-- END generate_readme.sh generated TOC please keep comment here to allow auto update -->
<!-- START generate_readme.sh generated SHDOC please keep comment here to allow auto update -->
<!-- DO NOT EDIT THIS SECTION, INSTEAD RE-RUN generate_readme.sh TO UPDATE -->
<!-- END generate_readme.sh generated SHDOC please keep comment here to allow auto update -->
## License
[MIT](https://github.com/labbots/google-drive-upload/blob/master/LICENSE)

196
bin/shdoc.awk Executable file
View File

@@ -0,0 +1,196 @@
#!/usr/bin/awk -f
BEGIN {
styles["h1", "from"] = ".*"
styles["h1", "to"] = "# &"
styles["h2", "from"] = ".*"
styles["h2", "to"] = "## &"
styles["h3", "from"] = ".*"
styles["h3", "to"] = "### &"
styles["h4", "from"] = ".*"
styles["h4", "to"] = "#### &"
styles["code", "from"] = ".*"
styles["code", "to"] = "```&"
styles["/code", "to"] = "```"
styles["argN", "from"] = "^(\\$[0-9]) (\\S+)"
styles["argN", "to"] = "**\\1** (\\2):"
styles["arg@", "from"] = "^\\$@ (\\S+)"
styles["arg@", "to"] = "**...** (\\1):"
styles["li", "from"] = ".*"
styles["li", "to"] = "- &"
styles["i", "from"] = ".*"
styles["i", "to"] = "_&_"
styles["anchor", "from"] = ".*"
styles["anchor", "to"] = "[&](#&)"
styles["exitcode", "from"] = "([>!]?[0-9]{1,3}) (.*)"
styles["exitcode", "to"] = "**\\1**: \\2"
}
function render(type, text) {
return gensub( \
styles[type, "from"],
styles[type, "to"],
"g",
text \
)
}
function reset() {
has_example = 0
has_args = 0
has_exitcode = 0
has_stdout = 0
}
/^[[:space:]]*# @internal/ {
is_internal = 1
}
/^[[:space:]]*# @file/ {
sub(/^[[:space:]]*# @file /, "")
filedoc = render("h1", $0) "\n"
}
/^[[:space:]]*# @brief/ {
sub(/^[[:space:]]*# @brief /, "")
filedoc = filedoc "\n" $0
}
/^[[:space:]]*# @description/ {
in_description = 1
in_example = 0
reset()
docblock = ""
}
in_description {
if (/^[^[[:space:]]*#]|^[[:space:]]*# @[^d]|^[[:space:]]*[^#]/) {
if (!match(docblock, /\n$/)) {
docblock = docblock "\n"
}
in_description = 0
} else {
sub(/^[[:space:]]*# @description /, "")
sub(/^[[:space:]]*# /, "")
sub(/^[[:space:]]*#$/, "")
docblock = docblock "\n" $0
}
}
in_example {
if (! /^[[:space:]]*#[ ]{3}/) {
in_example = 0
docblock = docblock "\n" render("/code") "\n"
} else {
sub(/^[[:space:]]*#[ ]{3}/, "")
docblock = docblock "\n" $0
}
}
/^[[:space:]]*# @example/ {
in_example = 1
docblock = docblock "\n" render("h3", "Example")
docblock = docblock "\n\n" render("code", "bash")
}
/^[[:space:]]*# @arg/ {
if (!has_args) {
has_args = 1
docblock = docblock "\n" render("h3", "Arguments") "\n\n"
}
sub(/^[[:space:]]*# @arg /, "")
$0 = render("argN", $0)
$0 = render("arg@", $0)
docblock = docblock render("li", $0) "\n"
}
/^[[:space:]]*# @noargs/ {
docblock = docblock "\n" render("i", "Function has no arguments.") "\n"
}
/^[[:space:]]*# @exitcode/ {
if (!has_exitcode) {
has_exitcode = 1
docblock = docblock "\n" render("h3", "Exit codes") "\n\n"
}
sub(/^[[:space:]]*# @exitcode /, "")
$0 = render("exitcode", $0)
docblock = docblock render("li", $0) "\n"
}
/^[[:space:]]*# @see/ {
sub(/[[:space:]]*# @see /, "")
$0 = render("anchor", $0)
$0 = render("li", $0)
docblock = docblock "\n" render("h4", "See also") "\n\n" $0 "\n"
}
/^[[:space:]]*# @stdout/ {
has_stdout = 1
sub(/^[[:space:]]*# @stdout /, "")
docblock = docblock "\n" render("h3", "Output on stdout")
docblock = docblock "\n\n" render("li", $0) "\n"
}
/^[ \t]*(function([ \t])+)?([a-zA-Z0-9_:-]+)([ \t]*)(\(([ \t]*)\))?[ \t]*\{/ && docblock != "" {
if (is_internal) {
is_internal = 0
} else {
func_name = gensub(\
/^[ \t]*(function([ \t])+)?([a-zA-Z0-9_:-]+)[ \t]*\(.*/, \
"\\3()", \
"g" \
)
doc = doc "\n" render("h2", func_name) "\n" docblock
url = func_name
# https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/toc_filter.rb#L44-L45
url = tolower(url)
gsub(/[^[:alnum:] -]/, "", url)
gsub(/ /, "-", url)
toc = toc "\n" "- [" func_name "](#" url ")"
}
docblock = ""
reset()
}
END {
if (filedoc != "") {
print filedoc
}
print toc
print ""
print doc
}

136
src/array.sh Normal file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/env bash
# @file Array
# @brief Functions for array operations and manipulations.
# @description Check if item exists in the given array.
#
# @example
# array=("a" "b" "c")
# array::contains "c" ${array[@]}
# #Output
# 0
#
# @arg $1 mixed Item to search (needle).
# @arg $2 array array to be searched (haystack).
#
# @exitcode 0 If successful.
# @exitcode 1 If no match found in the array.
# @exitcode 2 Function missing arguments.
array::contains() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
declare query="${1:-}"
shift
for element in "${@}"; do
[[ "${element}" == "${query}" ]] && return 0
done
return 1
}
# @description Remove duplicate items from the array
#
# @example
# array=("a" "b" "a" "c")
# printf "%s" "$(array::dedupe ${array[@]})"
# #Output
# a b c
#
# @arg $1 array Array to be deduped.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Deduplicated array.
array::dedupe() {
[[ $# = 0 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
declare -A arr_tmp
declare -a arr_unique
for i in "$@"; do
{ [[ -z ${i} || ${arr_tmp[${i}]} ]]; } && continue
arr_unique+=("${i}") && arr_tmp[${i}]=x
done
printf '%s\n' "${arr_unique[@]}"
}
# @description Join array elements with a string.
#
# @example
# array=("a" "b" "c" "d")
# printf "%s" "$(array::join "," "${array[@]}")"
# #Output
# a,b,c,d
# printf "%s" "$(array::join "" "${array[@]}")"
# #Output
# abcd
#
# @arg $1 string String to join the array elements (glue).
# @arg $2 array array to be joined with glue string.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout String containing a string representation of all the array elements in the same order,with the glue string between each element.
array::join() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
declare delimiter="${1}"
shift
printf "%s" "${1}"
shift
printf "%s" "${@/#/${delimiter}}"
}
# @description Return an array with elements in reverse order.
#
# @example
# array=(1 2 3 4 5)
# printf "%s" "$(array::reverse "${array[@]}")"
# #Output
# 5 4 3 2 1
#
# @arg $1 array The input array.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout The reversed array.
array::reverse() {
[[ $# = 0 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
declare min=0
declare -a array
array=("$@")
declare max=$((${#array[@]} - 1))
while [[ $min -lt $max ]]; do
# Swap current first and last elements
x="${array[$min]}"
array[$min]="${array[$max]}"
array[$max]="$x"
# Move closer
((min++, max--))
done
printf '%s\n' "${array[@]}"
}
# @description Returns a random item from the array.
#
# @example
# array=("a" "b" "c" "d")
# printf "%s\n" "$(array::random_element "${array[@]}")"
# #Output
# c
#
# @arg $1 array The input array.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Random item out of the array.
array::random_element() {
[[ $# = 0 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
declare -a array
local array=("$@")
printf '%s\n' "${array[RANDOM % $#]}"
}

196
src/string.sh Normal file
View File

@@ -0,0 +1,196 @@
#!/usr/bin/env bash
# @file String
# @brief Functions for string operations and manipulations.
# @description Strip whitespace from the beginning and end of a string.
#
# @example
# echo "$(string::trim " Hello World! ")"
# #Output
# Hello World!
#
# @arg $1 The string that will be trimmed.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout The trimmed string.
string::trim() {
[[ $# = 0 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
: "${1#"${1%%[![:space:]]*}"}"
: "${_%"${_##*[![:space:]]}"}"
printf '%s\n' "$_"
}
# @description Split a string to array by a delimiter.
#
# @example
# printf "%s" "$(string::split "Hello!World" "!")"
# #Output
# Hello
# World
#
# @arg $1 string The input string.
# @arg $2 string The delimiter string.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Returns an array of strings created by splitting the string parameter by the delimiter.
string::split() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
IFS=$'\n' read -d "" -ra arr <<< "${1//$2/$'\n'}"
printf '%s\n' "${arr[@]}"
}
# @description Strip characters from the beginning of a string.
#
# @example
# echo "$(string::lstrip "Hello World!" "He")"
# #Output
# llo World!
#
# @arg $1 string The input string.
# @arg $2 string The characters you want to strip.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Returns the modified string.
string::lstrip() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
printf '%s\n' "${1##$2}"
}
# @description Strip characters from the end of a string.
#
# @example
# echo "$(string::rstrip "Hello World!" "d!")"
# #Output
# Hello Worl
#
# @arg $1 string The input string.
# @arg $2 string The characters you want to strip.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Returns the modified string.
string::rstrip() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
printf '%s\n' "${1%%$2}"
}
# @description Make a string lowercase.
#
# @example
# echo "$(string::to_lower "HellO")"
# #Output
# hello
#
# @arg $1 string The input string.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Returns the lowercased string.
string::to_lower() {
[[ $# = 0 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
if [[ ${BASH_VERSINFO:-0} -ge 4 ]]; then
printf '%s\n' "${1,,}"
else
printf "${@}\n" | tr '[A-Z]' '[a-z]'
fi
}
# @description Make a string all uppercase.
#
# @example
# echo "$(string::to_upper "HellO")"
# #Output
# HELLO
#
# @arg $1 string The input string.
#
# @exitcode 0 If successful.
# @exitcode 2 Function missing arguments.
#
# @stdout Returns the uppercased string.
string::to_upper() {
[[ $# = 0 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
if [[ ${BASH_VERSINFO:-0} -ge 4 ]]; then
printf '%s\n' "${1^^}"
else
printf "${@}\n" | tr '[a-z]' '[A-Z]'
fi
}
# @description Check whether the search string exists within the input string.
#
# @example
# string::contains "Hello World!" "lo"
#
# @arg $1 string The input string.
# @arg $2 string The search key.
# @exitcode 0 If match found.
# @exitcode 1 If no match found.
# @exitcode 2 Function missing arguments.
string::contains() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
# Usage: string_contains hello he
[[ "${1}" == *${2}* ]]
}
# @description Check whether the input string starts with key string.
#
# @example
# string::starts_with "Hello World!" "He"
#
# @arg $1 string The input string.
# @arg $2 string The search key.
# @exitcode 0 If match found.
# @exitcode 1 If no match found.
# @exitcode 2 Function missing arguments.
string::starts_with() {
# Usage: string_starts_with hello he
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
[[ "${1}" == ${2}* ]]
}
# @description Check whether the input string ends with key string.
#
# @example
# string::ends_with "Hello World!" "d!"
#
# @arg $1 string The input string.
# @arg $2 string The search key.
# @exitcode 0 If match found.
# @exitcode 1 If no match found.
# @exitcode 2 Function missing arguments.
string::ends_with() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
# Usage: string_ends_wit hello lo
[[ "${1}" == *${2} ]]
}
# @description Check whether the input string matches the given regex.
#
# @example
# string::regex "HELLO" "^[A-Z]*$"
#
# @arg $1 string The input string.
# @arg $2 string The search key.
# @exitcode 0 If match found.
# @exitcode 1 If no match found.
# @exitcode 2 Function missing arguments.
string::regex() {
[[ $# -lt 2 ]] && printf "%s: Missing arguments\n" "${FUNCNAME[0]}" && return 2
if [[ ${1} =~ ${2} ]]; then
return 0
else
return 1
fi
}