From 39e386700a9ab483427a8f11c18b15a652843a3c Mon Sep 17 00:00:00 2001 From: rwxrob Date: Tue, 31 Aug 2021 09:47:57 -0400 Subject: [PATCH] Add `_urlencode` and `_newest` and better docs --- README.md | 100 ++++++++++++++++++++++++++++----------- cmd | 136 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 176 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index d4ee50e..66c4ea4 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,6 @@ gh repo create rwxrob/mycmd -p rwxrob/template-bash-command This `cmd` inside can then be renamed and finished. -Obviously, not all of this is needed for many Bash scripts. Just remove -what you do not need or want. If you want to keep a command but hide it -from users just add another underscore to the prefix which turns it into -a hidden command, which will not be included in help documentation and -tab completion, but will still be there. The `readme` command (which -generates this `README.md` file is a good candidate for this.) - -Be sure to check out the builtin and utility functions. Some of these -can be removed as well if you really want. - -The `_initialize` function is meant to put initialization code at the -beginning of the script to be found easily even though it is called at -the bottom of the script (as bash requires). - ## Naming Conventions * Name repos containing single bash commands with `cmd-` @@ -32,6 +18,76 @@ the bottom of the script (as bash requires). * Start command functions with `command_` to be completed * Start command functions with `command__` to not be completed +If you want to keep a command but hide it from users just add another +underscore to the prefix which turns it into a hidden command, which +will not be included in help documentation and tab completion, but will +still be there. + +## Builtins and Utilities + +A number of builtin and frequently used utility functions have been +included for convenience. These save developers from adding other +moronic things like `sed` and `awk` subprocesses. Obviously, not all of +this is needed for many Bash scripts. Just remove what you do not need +or want. + +### `_initialize` + +The `_initialize` function is meant to contain initialization code and +be placed at the beginning of the script to be found easily even though +it is called at the bottom of the script (as bash requires). + +### `_have` + +Returns true (0) if the first argument exists as an executable in the +current `PATH`. Otherwise, return false (1). + +### `_checkdep` + +Checks that the first argument exists as an executable in the current +`PATH`. If so, returns true (0). If not, prints a generic error message +in English and returns false (1). The "progressive enhancement" design +principle requires minimal functionality using what is available and +progressively upgrading based on what is detected. + +### `_newest` + +Uses `ls` to return the newest file or directory in the specified +directory. + +### `_trim` + +Removes all whitespace (`[:space:]`) from the beginning and ending +of a string without invoking a subprocess. + +### `_filter` + +Reads the first argument or each line of standard input passing +each individually as the first argument to the calling function one at +a time. The UNIX philosophy requires all commands be filters whenever possible. + +### `_buffer` + +Reads the first argument or all lines of standard input and then +passes them to the calling function as the first argument. The UNIX philosophy requires all command be filters whenever possible. + +### `_reduce` + +Takes the name of an array and a bash extended regular expression +and prints only the array entries that match, one to a line suitable for +converting back into an array with `IFS=$n` or just as an in-memory +`grep` replacement. + +### `_jsonstr` + +Encodes first argument or all standard input into a single line of JSON text. This function depends on the `jq` command. + +### `_urlencode` + +Encodes the first argument or all standard input using standard URL +encoding suitable for passing to `curl` or whatever. This function has +no external dependencies. + ## Dependencies Required: @@ -45,7 +101,7 @@ Optional: ## Justification -Bash is the dominate shell scripting language and the official default +Bash is the dominant shell scripting language and the official default Linux interactive shell, which reduces cognitive overhead; every command line *is* a line of code that could be put into script as is. Bash scripts are at the core of cloud, containers, and Kubernetes. Bash 4+ @@ -170,18 +226,6 @@ to `$PAGER` (default: more). Also see `readme` and `usage` commands. -## Convert to JSON String - -``` -cmd json STRING -cmd json <<< STRING -cmd json < FILE -cmd json < <(COMMAND) -``` - -Converts input into JSON string using `jq` (if found) containing only -escaped (`\n`) line returns. - ## Generate `README.md` File ``` @@ -202,5 +246,5 @@ Displays a summary of usage. ---- -*Autogenerated Sun Aug 29 12:18:52 PM EDT 2021* +*Autogenerated Tue Aug 31 09:47:12 AM EDT 2021* diff --git a/cmd b/cmd index 2d695c9..a9d8025 100755 --- a/cmd +++ b/cmd @@ -31,20 +31,6 @@ gh repo create rwxrob/mycmd -p rwxrob/template-bash-command This `cmd` inside can then be renamed and finished. -Obviously, not all of this is needed for many Bash scripts. Just remove -what you do not need or want. If you want to keep a command but hide it -from users just add another underscore to the prefix which turns it into -a hidden command, which will not be included in help documentation and -tab completion, but will still be there. The `readme` command (which -generates this `README.md` file is a good candidate for this.) - -Be sure to check out the builtin and utility functions. Some of these -can be removed as well if you really want. - -The `_initialize` function is meant to put initialization code at the -beginning of the script to be found easily even though it is called at -the bottom of the script (as bash requires). - ## Naming Conventions * Name repos containing single bash commands with `cmd-` @@ -52,6 +38,76 @@ the bottom of the script (as bash requires). * Start command functions with `command_` to be completed * Start command functions with `command__` to not be completed +If you want to keep a command but hide it from users just add another +underscore to the prefix which turns it into a hidden command, which +will not be included in help documentation and tab completion, but will +still be there. + +## Builtins and Utilities + +A number of builtin and frequently used utility functions have been +included for convenience. These save developers from adding other +moronic things like `sed` and `awk` subprocesses. Obviously, not all of +this is needed for many Bash scripts. Just remove what you do not need +or want. + +### `_initialize` + +The `_initialize` function is meant to contain initialization code and +be placed at the beginning of the script to be found easily even though +it is called at the bottom of the script (as bash requires). + +### `_have` + +Returns true (0) if the first argument exists as an executable in the +current `PATH`. Otherwise, return false (1). + +### `_checkdep` + +Checks that the first argument exists as an executable in the current +`PATH`. If so, returns true (0). If not, prints a generic error message +in English and returns false (1). The "progressive enhancement" design +principle requires minimal functionality using what is available and +progressively upgrading based on what is detected. + +### `_newest` + +Uses `ls` to return the newest file or directory in the specified +directory. + +### `_trim` + +Removes all whitespace (`[:space:]`) from the beginning and ending +of a string without invoking a subprocess. + +### `_filter` + +Reads the first argument or each line of standard input passing +each individually as the first argument to the calling function one at +a time. The UNIX philosophy requires all commands be filters whenever possible. + +### `_buffer` + +Reads the first argument or all lines of standard input and then +passes them to the calling function as the first argument. The UNIX philosophy requires all command be filters whenever possible. + +### `_reduce` + +Takes the name of an array and a bash extended regular expression +and prints only the array entries that match, one to a line suitable for +converting back into an array with `IFS=$'\n'` or just as an in-memory +`grep` replacement. + +### `_jsonstr` + +Encodes first argument or all standard input into a single line of JSON text. This function depends on the `jq` command. + +### `_urlencode` + +Encodes the first argument or all standard input using standard URL +encoding suitable for passing to `curl` or whatever. This function has +no external dependencies. + ## Dependencies Required: @@ -65,7 +121,7 @@ Optional: ## Justification -Bash is the dominate shell scripting language and the official default +Bash is the dominant shell scripting language and the official default Linux interactive shell, which reduces cognitive overhead; every command line *is* a line of code that could be put into script as is. Bash scripts are at the core of cloud, containers, and Kubernetes. Bash 4+ @@ -224,6 +280,9 @@ command_readme() { echo -e "----\n\n*Autogenerated $(date)*\n" } +# command_json() { _jsonstr "$@"; } +# command_urlencode() { _urlencode "$@"; } + # -------------------------- config command -------------------------- HELP[config]=' @@ -377,29 +436,30 @@ _config_dump() { <(printf "%s\n" "${CONFIG[@]}") } -# --------------------------- json command --------------------------- - -HELP[json]=' -## Convert to JSON String - -``` -'"$EXE"' json STRING -'"$EXE"' json <<< STRING -'"$EXE"' json < FILE -'"$EXE"' json < <(COMMAND) -``` - -Converts input into JSON string using `jq` (if found) containing only -escaped (`\\n`) line returns.' - -command_json() { _jsonstr "$@"; } +# ----------------------------- utilities ---------------------------- _jsonstr() { + _checkdep jq _buffer "$@" && return $? jq -MRsc <<< "$1" } -# ----------------------------- utilities ---------------------------- +_urlencode() { + _buffer "$@" && return $? + local string="$1" + local strlen=${#string} + local encoded="" + local pos c o + for ((pos = 0; pos < strlen; pos++)); do + c=${string:$pos:1} + case "$c" in + [-_.~a-zA-Z0-9]) o="$c" ;; + *) printf -v o '%%%02x' "'$c'" ;; + esac + encoded+="$o" + done + echo "$encoded" +} _reduce() { local -n name="${1:?"name of array required"}" @@ -408,6 +468,12 @@ _reduce() { done < <(printf "%s\n" "${name[@]}") } +_newest() { + IFS=$'\n' + local -a f=($(ls -1 --color=never -trd ${1:-.}/* 2>/dev/null)) + [[ ${#f} > 0 ]] && echo "${f[-1]}" +} + _trim() { local it="${1#"${1%%[![:space:]]*}"}" echo -e "${it%"${it##*[![:space:]]}"}" @@ -415,6 +481,12 @@ _trim() { _have(){ type "$1" &>/dev/null; } +_checkdep() { + _have "$1" && return 0 + echo "'$EXE' depends on '$1' for this, but not found" + return 1 +} + _filter(){ [[ -n "$1" ]] && return 1 while IFS= read -ra args; do