2021-08-15 15:56:56 -05:00
|
|
|
#!/bin/bash
|
2021-08-06 12:48:11 -05:00
|
|
|
# shellcheck disable=SC2016
|
|
|
|
set -e
|
2021-08-15 15:56:56 -05:00
|
|
|
# export PATH="/bin:/usr/bin:/usr/local/bin" # safer, if you can
|
2021-08-06 12:48:11 -05:00
|
|
|
|
|
|
|
(( BASH_VERSINFO[0] < 4 )) && echo "Bash 4+ required." && exit 1
|
|
|
|
|
|
|
|
: "${PAGER:=more}"
|
|
|
|
: "${EDITOR:=vi}"
|
|
|
|
: "${HELP_BROWSER:=}"
|
2021-08-23 21:10:27 -05:00
|
|
|
: "${EXE:="${0##*/}"}"
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
declare -A HELP
|
|
|
|
declare -A CONFIG
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-27 12:11:03 -05:00
|
|
|
_initialize() {
|
|
|
|
: # put initialization code here
|
|
|
|
}
|
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[main]='
|
2021-08-10 17:47:07 -05:00
|
|
|
# Bash Template Command
|
|
|
|
|
|
|
|
*This `README.md` is autogenerated.*
|
|
|
|
|
|
|
|
This is a GitHub template repo that will be copied instead of forked to
|
|
|
|
create a new Bash command with a command something like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
gh repo create rwxrob/mycmd -p rwxrob/template-bash-command
|
|
|
|
```
|
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
This `cmd` inside can then be renamed and finished.
|
2021-08-10 17:47:07 -05:00
|
|
|
|
2021-08-27 08:16:21 -05:00
|
|
|
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.
|
2021-08-10 17:47:07 -05:00
|
|
|
|
2021-08-27 12:11:03 -05:00
|
|
|
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).
|
|
|
|
|
2021-08-10 17:47:07 -05:00
|
|
|
## Naming Conventions
|
|
|
|
|
|
|
|
* Name repos containing single bash commands with `cmd-`
|
|
|
|
* Name template repos beginning with `template-`
|
|
|
|
* Start command functions with `command_` to be completed
|
|
|
|
* Start command functions with `command__` to not be completed
|
|
|
|
|
|
|
|
## Dependencies
|
|
|
|
|
|
|
|
Required:
|
|
|
|
|
|
|
|
* Bash 4+
|
|
|
|
|
|
|
|
Optional:
|
|
|
|
|
|
|
|
* `pandoc` - for rich help docs
|
2021-08-23 21:10:27 -05:00
|
|
|
* `jq` - for `json` and anything that uses it
|
2021-08-10 17:47:07 -05:00
|
|
|
|
|
|
|
## Justification
|
|
|
|
|
|
|
|
Bash is the dominate 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+
|
|
|
|
with its associative array support, powerful regular expressions, and
|
|
|
|
multiple ways of feeding data to loops easily covers the needs
|
|
|
|
previously requiring Python and Perl scripts. Bash scripts are also much
|
|
|
|
more powerful, safer, flexible, and performant than POSIX shell or Zsh.
|
2021-08-10 13:18:33 -05:00
|
|
|
|
2021-08-10 18:37:51 -05:00
|
|
|
## Guidelines
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-10 18:37:51 -05:00
|
|
|
* Write GitHub Flavored Markdown only
|
|
|
|
* Use present tense ("outputs" over "will output")
|
2021-08-23 21:10:27 -05:00
|
|
|
* Prefer term "output" and "display" over ~~print~~
|
2021-08-10 18:37:51 -05:00
|
|
|
* Follow the [naming conventions](#naming-conventions)
|
2021-08-15 15:56:56 -05:00
|
|
|
* Use the official bash path: `#!/bin/bash`
|
|
|
|
* Use of `#!/usr/bin/bash` is outdated
|
2021-08-10 17:47:07 -05:00
|
|
|
* Using `#!/usr/bin/env bash` introduces unnecessary risk
|
2021-08-10 18:37:51 -05:00
|
|
|
* Explicitly export `PATH` in script when possible
|
2021-08-10 17:47:07 -05:00
|
|
|
* Always check script with [`shellcheck`] before releasing
|
|
|
|
* Always use `bc` for *any* floating point math
|
|
|
|
|
|
|
|
[`shellcheck`]: <https://www.shellcheck.net>
|
|
|
|
|
|
|
|
## Legal
|
|
|
|
|
|
|
|
Copyright 2021 Rob Muhlestein <rob@rwx.gg>
|
|
|
|
Released under Apache-2.0 License
|
2021-08-23 21:27:03 -05:00
|
|
|
Please mention rwxrob.tv'
|
2021-08-10 17:47:07 -05:00
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[foo]='Foos things.'
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-10 11:52:00 -05:00
|
|
|
command_foo() {
|
2021-08-06 12:48:11 -05:00
|
|
|
_filter "$@" && return $?
|
|
|
|
echo "would foo: $*"
|
|
|
|
}
|
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[bar]='
|
|
|
|
|
|
|
|
```
|
|
|
|
'"$EXE"' bar
|
|
|
|
```
|
|
|
|
|
|
|
|
Bar the things.'
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-10 11:52:00 -05:00
|
|
|
command_bar() {
|
2021-08-09 18:01:36 -05:00
|
|
|
_buffer "$@" && return $?
|
2021-08-06 12:48:11 -05:00
|
|
|
echo "would bar: $*"
|
|
|
|
}
|
2021-08-29 11:19:03 -05:00
|
|
|
|
|
|
|
HELP[some.config.setting]='Get and set `some.config.setting`.'
|
|
|
|
|
|
|
|
command_some.config.setting() {
|
|
|
|
command_config some.config.setting "$@"
|
|
|
|
}
|
|
|
|
|
2021-08-10 11:52:00 -05:00
|
|
|
command__hidden() {
|
2021-08-06 12:48:11 -05:00
|
|
|
_filter "$@" && return $?
|
|
|
|
echo "would run _hidden: $*"
|
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
############################## BOILERPLATE ###########################
|
|
|
|
## Everything from here to the end of file can be snipped and updated
|
|
|
|
## with latest from https://github.com/rwxrob/template-bash-command.
|
|
|
|
|
|
|
|
# -------------------------- utility command -------------------------
|
2021-08-10 17:59:04 -05:00
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[usage]='Displays a summary of usage.'
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-10 11:52:00 -05:00
|
|
|
command_usage() {
|
2021-08-06 12:48:11 -05:00
|
|
|
local -a cmds
|
|
|
|
for c in "${COMMANDS[@]}"; do
|
|
|
|
[[ ${c:0:1} =~ _ ]] && continue
|
2021-08-18 19:54:40 -05:00
|
|
|
cmds+=("$c")
|
2021-08-06 12:48:11 -05:00
|
|
|
done
|
2021-08-18 19:54:40 -05:00
|
|
|
local IFS='|'
|
|
|
|
printf "usage: %s (%s)\n" "$EXE" "${cmds[*]}"
|
2021-08-06 12:48:11 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
# --------------------------- help command ---------------------------
|
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[help]='
|
2021-08-10 17:47:07 -05:00
|
|
|
|
|
|
|
```
|
2021-08-23 21:10:27 -05:00
|
|
|
'"$EXE"' help [COMMAND]
|
2021-08-10 17:47:07 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
Displays specific help information. If no argument is passed displays
|
|
|
|
general help information (main). Otherwise, the documentation for the
|
|
|
|
specific argument keyword is displayed, which usually corresponds to
|
2021-08-23 21:10:27 -05:00
|
|
|
a COMMAND name (but not necessarily). All documentation is written in
|
2021-08-10 17:47:07 -05:00
|
|
|
GitHub Flavored Markdown and will displayed as a web page if `pandoc`
|
|
|
|
and `$HELP_BROWSER` are detected, otherwise, just the Markdown is sent
|
|
|
|
to `$PAGER` (default: more).
|
2021-08-10 13:18:33 -05:00
|
|
|
|
2021-08-10 17:47:07 -05:00
|
|
|
Also see `readme` and `usage` commands.
|
|
|
|
'
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-10 11:52:00 -05:00
|
|
|
command_help() {
|
2021-08-18 19:54:40 -05:00
|
|
|
local name="${1:-main}" title body
|
2021-08-10 20:59:02 -05:00
|
|
|
title=$(_help_title "$name") || true
|
2021-08-10 13:18:33 -05:00
|
|
|
if [[ -z "$title" ]]; then
|
2021-08-23 21:10:27 -05:00
|
|
|
body="${HELP[$name]}"
|
2021-08-10 13:18:33 -05:00
|
|
|
title="$EXE $name"
|
|
|
|
[[ $name = main ]] && title="$EXE"
|
|
|
|
else
|
2021-08-23 21:10:27 -05:00
|
|
|
body="${HELP[$name]}"
|
2021-08-10 13:18:33 -05:00
|
|
|
body=${body#*$title}
|
|
|
|
fi
|
2021-08-06 12:48:11 -05:00
|
|
|
local file="/tmp/help-$EXE-$name.html"
|
|
|
|
if _have pandoc ; then
|
|
|
|
if _have "$HELP_BROWSER" && [[ -t 1 ]] ;then
|
2021-08-23 21:10:27 -05:00
|
|
|
pandoc -f gfm -s --metadata title="$title" \
|
2021-08-10 13:18:33 -05:00
|
|
|
-o "$file" <<< "$body"
|
2021-08-06 12:48:11 -05:00
|
|
|
[[ -z "$2" ]] && cd /tmp && exec "$HELP_BROWSER" "$file"
|
|
|
|
return 0
|
|
|
|
fi
|
2021-08-23 21:10:27 -05:00
|
|
|
pandoc -f gfm -s --metadata title="$title" \
|
2021-08-10 13:18:33 -05:00
|
|
|
-t plain <<< "$body" | "$PAGER"
|
2021-08-06 12:48:11 -05:00
|
|
|
return 0
|
|
|
|
fi
|
2021-08-10 13:18:33 -05:00
|
|
|
echo -e "$title\n\n$body" | "$PAGER"
|
2021-08-06 12:48:11 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_help_title() {
|
|
|
|
_filter "$@" && return $?;
|
|
|
|
local name="$1"
|
|
|
|
while IFS= read -r line; do
|
|
|
|
[[ $line =~ ^[:space]*$ ]] && continue
|
|
|
|
[[ $line =~ ^#\ (.+) ]] && echo "${BASH_REMATCH[1]}" && return 0
|
|
|
|
return 1
|
|
|
|
done <<< "${HELP[$name]}"
|
|
|
|
}
|
|
|
|
|
|
|
|
# -------------------------- readme command --------------------------
|
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[readme]='
|
|
|
|
## Generate `README.md` File
|
2021-08-10 17:47:07 -05:00
|
|
|
|
|
|
|
```
|
2021-08-23 21:10:27 -05:00
|
|
|
'"$EXE"' readme > README.md
|
2021-08-10 17:47:07 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
The `readme` command will output the embedded help documentation in raw
|
|
|
|
GitHub Flavored Markdown suitable for use as a `README.md` file on
|
2021-08-23 21:10:27 -05:00
|
|
|
GitHub or similar hosting service.'
|
2021-08-10 17:47:07 -05:00
|
|
|
|
|
|
|
command_readme() {
|
2021-08-23 21:10:27 -05:00
|
|
|
_trim "${HELP[main]}"
|
|
|
|
echo
|
2021-08-10 17:47:07 -05:00
|
|
|
while IFS= read -r name; do
|
|
|
|
[[ $name = main ]] && continue
|
2021-08-23 21:10:27 -05:00
|
|
|
body=$(_trim "${HELP[$name]}")
|
|
|
|
[[ $body =~ ^\# ]] || body="## The \`$name\` Command"$'\n\n'$body
|
|
|
|
printf "%s\n\n" "$body"
|
|
|
|
done < <(printf "%s\n" "${!HELP[@]}" | LC_COLLATE=C sort)
|
2021-08-10 18:15:33 -05:00
|
|
|
echo -e "----\n\n*Autogenerated $(date)*\n"
|
2021-08-10 17:47:07 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
# -------------------------- config command --------------------------
|
|
|
|
|
2021-08-23 21:10:27 -05:00
|
|
|
HELP[config]='
|
|
|
|
|
|
|
|
```
|
|
|
|
'"$EXE"' config
|
|
|
|
'"$EXE"' config KEY
|
|
|
|
'"$EXE"' config KEY VALUE
|
2021-08-24 23:50:39 -05:00
|
|
|
'"$EXE"' config KEY ""
|
2021-08-29 11:19:03 -05:00
|
|
|
'"$EXE"' config keys
|
|
|
|
'"$EXE"' config val[ues]
|
|
|
|
'"$EXE"' config dir[ectory]
|
2021-08-26 08:15:09 -05:00
|
|
|
'"$EXE"' config path [file]
|
|
|
|
'"$EXE"' config edit [file]
|
2021-08-29 11:19:03 -05:00
|
|
|
'"$EXE"' config del[ete]
|
2021-08-23 21:10:27 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
The `config` command is for reading, writing, and displaying standard
|
2021-08-24 23:50:39 -05:00
|
|
|
open desktop configuration properties. Pass an empty string to delete
|
|
|
|
a property.
|
2021-08-23 21:10:27 -05:00
|
|
|
|
|
|
|
### Arguments
|
|
|
|
|
|
|
|
With no arguments outputs all the currently cached configuration
|
2021-08-29 11:19:03 -05:00
|
|
|
settings.
|
2021-08-23 21:10:27 -05:00
|
|
|
|
|
|
|
With a single KEY argument fetches the value for that key and outputs
|
2021-08-26 08:15:09 -05:00
|
|
|
it unless it is one of the following special (reserved) key names:
|
|
|
|
|
2021-08-29 11:19:03 -05:00
|
|
|
* `dir*` full path to config directory
|
2021-08-26 08:15:09 -05:00
|
|
|
* `path` full path to specific config file (default: `values`)
|
|
|
|
* `edit` opens config file in editor (default: `editor` or `$EDITOR)
|
2021-08-29 11:19:03 -05:00
|
|
|
* `keys` output the configuration keys, one per line
|
|
|
|
* `val*` output the configuration values, one per line
|
|
|
|
* `del*` if key argument then delete a specific key, otherwise prompt
|
2021-08-23 21:10:27 -05:00
|
|
|
|
|
|
|
With more than one argument the remaining arguments after the KEY will
|
2021-08-23 21:21:28 -05:00
|
|
|
be combined into the VALUE and written to a `values` file in the
|
2021-08-24 23:50:39 -05:00
|
|
|
configuration directory.
|
|
|
|
|
|
|
|
### Configuration Directory
|
|
|
|
|
|
|
|
The configuration directory path relies on the following environment
|
|
|
|
variables:
|
|
|
|
|
|
|
|
* `EXE` - defaults to name of currently running command ('"$EXE"')
|
|
|
|
* `HOME` - checked for `$HOME/.config/$EXE/values`
|
|
|
|
* `XDG_CONFIG_HOME` - overrides `$HOME/.config`
|
|
|
|
* `CONFIG_DIR` - full path to directory containing `values` file
|
|
|
|
|
|
|
|
The `CONFIG_DIR` always takes priority over anything else if set, but is
|
|
|
|
never implied. If the directory does not exist it will be created the
|
|
|
|
first time a value is set.
|
2021-08-23 21:10:27 -05:00
|
|
|
|
2021-08-23 21:22:43 -05:00
|
|
|
### Configuration `values` File Format
|
2021-08-23 21:10:27 -05:00
|
|
|
|
2021-08-23 21:21:28 -05:00
|
|
|
The file (which is almost always located at
|
|
|
|
`~/.config/'"$EXE"'/values`) uses the simplest possible format to
|
2021-08-23 21:14:42 -05:00
|
|
|
facilitate standard UNIX parsing and filtering with any number of
|
|
|
|
existing tools (and no `jq` dependency).
|
2021-08-23 21:10:27 -05:00
|
|
|
|
|
|
|
* One KEY=VALUE per line
|
|
|
|
* KEYs may be anything but the equal sign (`=`)
|
|
|
|
* VALUEs may be anything but line returns must be escaped
|
|
|
|
|
|
|
|
Note that this is *not* the same as Java properties and other similar
|
|
|
|
format. It is designed for ultimate simplicity, efficiency, and
|
|
|
|
portability.'
|
|
|
|
|
|
|
|
command_config() {
|
2021-08-26 08:15:09 -05:00
|
|
|
case $1 in
|
2021-08-29 11:19:03 -05:00
|
|
|
dir*) shift; _config_dir "$@"; return $? ;;
|
|
|
|
path) shift; _config_path "$@"; return $? ;;
|
|
|
|
edit) shift; _config_edit "$@"; return $? ;;
|
|
|
|
del*) shift; _config_del "$@"; return $? ;;
|
|
|
|
keys) shift; _config_keys "$@"; return $? ;;
|
|
|
|
val*) shift; _config_vals "$@"; return $? ;;
|
2021-08-26 08:15:09 -05:00
|
|
|
esac
|
2021-08-23 21:10:27 -05:00
|
|
|
case $# in
|
2021-08-26 08:15:09 -05:00
|
|
|
0) _config_dump ;;
|
|
|
|
1) _config_get "$@" ;;
|
|
|
|
*) _config_set "$@" ;;
|
2021-08-23 21:10:27 -05:00
|
|
|
esac
|
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_edit() {
|
|
|
|
: "${CONFIG[editor]:="${EDITOR:=vi}"}"
|
|
|
|
exec "${CONFIG[editor]}" "$(_config_path "${1:-values}")"
|
2021-08-23 21:10:27 -05:00
|
|
|
}
|
|
|
|
|
2021-08-29 11:19:03 -05:00
|
|
|
_config_del() {
|
|
|
|
if [[ -z "$1" ]];then
|
|
|
|
select key in "${!CONFIG[@]}"; do
|
|
|
|
_config_del "$key"
|
|
|
|
return $?
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
_config_set "$1" ''
|
|
|
|
}
|
|
|
|
|
|
|
|
_config_keys() { printf "%s\n" "${!CONFIG[@]}"; }
|
|
|
|
|
|
|
|
_config_vals() { printf "%s\n" "${CONFIG[@]}"; }
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_dir() {
|
2021-08-24 23:50:39 -05:00
|
|
|
local dir="$HOME/.config/$EXE"
|
|
|
|
[[ -n "$XDG_CONFIG_HOME" ]] && dir="$XDG_CONFIG_HOME/$EXE"
|
|
|
|
[[ -n "$CONFIG_DIR" ]] && dir="$CONFIG_DIR"
|
|
|
|
[[ -n "$1" ]] && echo "$dir/$1" && return 0
|
2021-08-26 09:00:16 -05:00
|
|
|
printf "%s" "$dir"
|
|
|
|
[[ -t 1 ]] && echo
|
2021-08-24 23:50:39 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_path() {
|
|
|
|
local file=${1:-values}
|
2021-08-26 09:00:16 -05:00
|
|
|
printf "%s/%s" "$(_config_dir)" "$file"
|
|
|
|
[[ -t 1 ]] && echo
|
2021-08-26 08:15:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
_config_set() {
|
2021-08-23 21:10:27 -05:00
|
|
|
local key="$1"; shift; local val="$*"
|
|
|
|
val="${val//$'\n'/\\n}"
|
|
|
|
CONFIG["$key"]="$val"
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_write
|
2021-08-23 21:10:27 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:48:11 -05:00
|
|
|
_config_get() {
|
|
|
|
printf "${CONFIG[$1]}"
|
|
|
|
[[ -t 1 ]] && echo
|
|
|
|
}
|
2021-08-24 23:50:39 -05:00
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_read() {
|
|
|
|
local values="$(_config_path)"
|
2021-08-24 23:50:39 -05:00
|
|
|
[[ -r "$values" ]] || return 0
|
2021-08-23 21:10:27 -05:00
|
|
|
while IFS= read -r line; do
|
|
|
|
[[ $line =~ ^([^=]+)=(.+)$ ]] || continue
|
|
|
|
CONFIG["${BASH_REMATCH[1]}"]="${BASH_REMATCH[2]}"
|
2021-08-24 23:50:39 -05:00
|
|
|
done < "$values"
|
2021-08-23 21:10:27 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_write() {
|
|
|
|
local dir="$(_config_dir)"
|
2021-08-24 23:50:39 -05:00
|
|
|
mkdir -p "$dir"
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_dump > "$dir/values"
|
2021-08-23 21:10:27 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
_config_dump() {
|
2021-08-23 22:06:47 -05:00
|
|
|
(( ${#CONFIG[@]} == 0 )) && return 0
|
2021-08-23 21:10:27 -05:00
|
|
|
paste -d=\
|
|
|
|
<(printf "%s\n" "${!CONFIG[@]}") \
|
|
|
|
<(printf "%s\n" "${CONFIG[@]}")
|
2021-08-10 13:18:33 -05:00
|
|
|
}
|
2021-08-06 12:48:11 -05:00
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
# --------------------------- 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 "$@"; }
|
2021-08-10 17:47:07 -05:00
|
|
|
|
2021-08-10 20:32:51 -05:00
|
|
|
_jsonstr() {
|
|
|
|
_buffer "$@" && return $?
|
|
|
|
jq -MRsc <<< "$1"
|
|
|
|
}
|
|
|
|
|
2021-08-26 08:15:09 -05:00
|
|
|
# ----------------------------- utilities ----------------------------
|
|
|
|
|
|
|
|
_reduce() {
|
|
|
|
local -n name="${1:?"name of array required"}"
|
|
|
|
while IFS= read -r key; do
|
|
|
|
[[ $key =~ $2 ]] && echo "$key"
|
|
|
|
done < <(printf "%s\n" "${name[@]}")
|
|
|
|
}
|
|
|
|
|
|
|
|
_trim() {
|
|
|
|
local it="${1#"${1%%[![:space:]]*}"}"
|
|
|
|
echo -e "${it%"${it##*[![:space:]]}"}"
|
|
|
|
}
|
|
|
|
|
2021-08-06 12:48:11 -05:00
|
|
|
_have(){ type "$1" &>/dev/null; }
|
|
|
|
|
|
|
|
_filter(){
|
|
|
|
[[ -n "$1" ]] && return 1
|
|
|
|
while IFS= read -ra args; do
|
|
|
|
"${FUNCNAME[1]}" "${args[@]}"
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:01:36 -05:00
|
|
|
_buffer() {
|
2021-08-06 12:48:11 -05:00
|
|
|
[[ -n "$1" ]] && return 1
|
|
|
|
"${FUNCNAME[1]}" "$(</dev/stdin)"
|
|
|
|
}
|
|
|
|
|
2021-08-10 17:59:04 -05:00
|
|
|
# --------------------- completion and delegation --------------------
|
2021-08-27 12:15:05 -05:00
|
|
|
# `complete -C foo foo` > `source <(foo bloated_completion)`
|
2021-08-10 17:59:04 -05:00
|
|
|
|
2021-08-06 12:48:11 -05:00
|
|
|
while IFS= read -r line; do
|
2021-08-10 11:52:00 -05:00
|
|
|
[[ $line =~ ^declare\ -f\ command_ ]] || continue
|
|
|
|
COMMANDS+=( "${line##declare -f command_}" )
|
2021-08-06 12:48:11 -05:00
|
|
|
done < <(declare -F)
|
2021-08-18 19:54:40 -05:00
|
|
|
mapfile -t COMMANDS < \
|
|
|
|
<(LC_COLLATE=C sort < <(printf "%s\n" "${COMMANDS[@]}"))
|
2021-08-06 12:48:11 -05:00
|
|
|
|
|
|
|
if [[ -n $COMP_LINE ]]; then
|
|
|
|
line=${COMP_LINE#* }
|
|
|
|
for c in "${COMMANDS[@]}"; do
|
|
|
|
[[ ${c:0:${#line}} == "${line,,}" && ${c:0:1} != _ ]] && echo "$c"
|
|
|
|
done
|
|
|
|
exit
|
|
|
|
fi
|
|
|
|
|
2021-08-27 12:11:03 -05:00
|
|
|
_config_read
|
|
|
|
_initialize
|
|
|
|
|
2021-08-06 12:48:11 -05:00
|
|
|
for c in "${COMMANDS[@]}"; do
|
|
|
|
if [[ $c == "$EXE" ]]; then
|
2021-08-10 11:52:00 -05:00
|
|
|
"command_$EXE" "$@"
|
2021-08-06 12:48:11 -05:00
|
|
|
exit $?
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
|
|
if [[ -n "$1" ]]; then
|
|
|
|
declare cmd="$1"; shift
|
|
|
|
for c in "${COMMANDS[@]}"; do
|
|
|
|
if [[ $c == "$cmd" ]]; then
|
2021-08-10 11:52:00 -05:00
|
|
|
"command_$cmd" "$@"
|
2021-08-06 12:48:11 -05:00
|
|
|
exit $?
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
|
2021-08-10 11:52:00 -05:00
|
|
|
command_usage "$@"
|