mirror of
https://github.com/armbian/bash-util.git
synced 2026-01-06 10:37:49 -08:00
Initial commit
This commit is contained in:
22
.editorconfig
Normal file
22
.editorconfig
Normal 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
7
.remarkrc
Normal 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
76
CODE_OF_CONDUCT.md
Normal 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
4
CONTRIBUTING.md
Normal 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
4
bash_utilities.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source src/array.sh
|
||||
source src/string.sh
|
||||
156
bin/generate_readme.sh
Executable file
156
bin/generate_readme.sh
Executable 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
21
bin/readme-template.md
Normal 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&utm_medium=referral&utm_content=labbots/bash-utility&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
196
bin/shdoc.awk
Executable 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
136
src/array.sh
Normal 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
196
src/string.sh
Normal 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
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user