Skip to content

Instantly share code, notes, and snippets.

@crshnbrn66
Forked from szampardi/cortex.sh
Created September 21, 2023 17:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crshnbrn66/850b1c963c6710b8f7042fc12689de64 to your computer and use it in GitHub Desktop.
Save crshnbrn66/850b1c963c6710b8f7042fc12689de64 to your computer and use it in GitHub Desktop.
use a json file for shell vars
#!/usr/bin/env bash
CORTEX_CRYPTO="aes-256-cbc"
_cortex_logger() { printf '{%s}[%s](%s):%s\t%s\n' "$(date -u '+%FT%TZ')" "${1}" "$(caller)" "${2}" "${@:3}" 1>&2; }
_cortex_io() {
case "${CORTEX_FILE}" in
"") _cortex_logger "$(caller)" ERR "CORTEX_FILE environment variable is empty/unset" && return 127 ;;
*)
if [[ -v CORTEX_KEY ]]; then
case "${1}" in
load) openssl enc -"${CORTEX_CRYPTO}" -pass env:CORTEX_KEY -pbkdf2 -d -in "${CORTEX_FILE}" ;;
save) openssl enc -"${CORTEX_CRYPTO}" -pass env:CORTEX_KEY -pbkdf2 -e -out "${CORTEX_FILE}" ;;
test) touch "${CORTEX_FILE}" && [[ -s ${CORTEX_FILE} ]] ;;
esac
else
case "${1}" in
load) cat "${CORTEX_FILE}" ;;
save) cat >"${CORTEX_FILE}" ;;
test) touch "${CORTEX_FILE}" && [[ -s ${CORTEX_FILE} ]] ;;
*) return 127 ;;
esac
fi
;;
esac
}
_cortex_setup() {
declare -x CORTEX_FILE
if [[ -z ${CORTEX_FILE} ]]; then CORTEX_FILE=$(mktemp); fi
if ! _cortex_io test; then printf '{}' | _cortex_io save || {
_cortex_logger "$(caller)" ERR "cannot read/write ${CORTEX_FILE}, exiting" && return 2
}; else
_cortex_logger "$(caller)" ERR "${CORTEX_FILE}, already exists and is not empty, exiting"
fi
}
_cortex_man() {
printf '
[cortex]
jq wrapper for shell env values with file-based persistence
[cmd] [desc] [example]
export print key="value" for matched path cortex export .path.to.ipinfo
find find keys cortex find hostname
init initialize/reset json cortex init my-idenfitier
list list path cortex list .path.to.ipinfo
read read key cortex read path to ipinfo
rm remove keys cortex rm $(cortex find hostname)
write write value (${@:2} or stdin) to key ($1) curl ipinfo.io | cortex write .path.to.ipinfo
[opt] [desc] [example]
-f specify json target curl ipinfo.io | cortex -f /dev/stdin ls
-e (export mode only) type of export (array or full) cortex -e array export path to ipinfo
' 1>&2
}
_cortex_jq_path() {
local _path _num
for i in "${@}"; do
_num=$(awk '{if ($1 == $1 + 0) print}' <<<"${i}")
case "${i}" in
"") continue ;;
"${_num}") printf -v _path '%s[%s]' "${_path}" "${_num}" ;;
*) printf -v _path '%s.%s' "${_path}" "${i}" ;;
esac
done
printf '%s' "${_path}"
}
_cortex_modify_json() {
local _json_val _jq_arg="arg" _verb="=" _v='$v'
if [[ -z ${1} ]]; then return 127; fi
local _key="${2}"
if [[ -z ${_key} ]]; then _cortex_logger "$(caller)" ERR "cannot set value with empty key" && return 127; fi
local _val="${3}"
if [[ -z ${_val} ]]; then
if [[ -t 0 ]]; then _cortex_logger "$(caller)" ERR "empty value and no readable input" && return 128; else
_val="$(tee)"
if [[ -z ${_val} ]]; then
_cortex_logger "$(caller)" ERR "empty value and no readable input"
return 128
fi
fi
fi
if [[ ${1} == a* ]]; then _verb='+' _v='[$v]'; fi
_json_val=$(jq -cMrn --argjson v "${_val}" '{ v: $v } | .v' 2>/dev/null || true)
if [[ -n ${_json_val} ]]; then _jq_arg="argjson"; fi
case "${_key}" in
"") return 127 ;;
"."* | "["*)
case "${_json_val}" in
"null") jq -cM --arg k "${_key}" "del(${_key})" ;;
"") jq -cM --"${_jq_arg}" v "${_val}" "${_key} ${_verb} ${_v}" ;;
*) jq -cM --"${_jq_arg}" v "${_json_val}" "${_key} ${_verb} ${_v}" ;;
esac
;;
*)
case "${_json_val}" in
"null") jq -cM --arg k "${_key}" 'del(.[$k])' ;;
"") jq -cM --arg k "${_key}" --"${_jq_arg}" v "${_val}" ".[\$k] ${_verb} ${_v}" ;;
*) jq -cM --arg k "${_key}" --"${_jq_arg}" v "${_json_val}" ".[\$k] ${_verb} ${_v}" ;;
esac
;;
esac <<<"$(_cortex_io load || printf '{}')"
}
cortex() {
local OPTIND _dump _export_type
while getopts ":e:f:" _opt; do
case "${_opt}" in
e) _export_type="${OPTARG}" ;;
f) CORTEX_FILE="${OPTARG}" ;;
*) _cortex_man && return 127 ;;
esac
done
shift $((OPTIND - 1))
case "${1}" in
"help" | h*) _cortex_man ;;
"export" | e*)
case "${_export_type}" in
"array" | a*) cortex get "${@:2}" | jq -cMr 'to_entries | map("[\(.key|@sh)]=\(.value|tostring|@sh)") | .[]' ;;
"full" | f*) cortex get "${@:2}" | jq -cMr 'paths(scalars) as $p | [([$p[]|tostring]|join("_")), (getpath($p)|tostring|@sh)] | join("=")' ;;
*) cortex get "${@:2}" | jq -cMr 'to_entries | map("\(.key)=\(.value|tostring|@sh)") | .[]' ;;
esac
;;
"find" | f*)
if [[ ${#@} -gt 1 ]]; then
_dump="$(_cortex_io load)"
for i in "${@:2}"; do
jq -cMr 'paths | select(.[-1] == "'"${i}"'") | @tsv' <<<"${_dump}" | while read -r line; do printf '%s\n' "$(_cortex_jq_path ${line})"; done
done
else
_cortex_io load | jq -cMr 'paths | @tsv' | while read -r line; do printf '%s\n' "$(_cortex_jq_path ${line})"; done
fi
;;
"init")
if _cortex_setup; then
jq -cMrn --arg id "${2:-"${0}"}" --arg ts "$(date -u '+%FT%TZ')" '{"id":$id,"timestamp":$ts}' | _cortex_io save
fi
;;
"list" | "ls" | l*)
case ${#@} in
1) _cortex_io load | jq -rS 'keys[]' ;;
2)
case "${2}" in
"") _cortex_logger "$(caller)" ERR "cannot list value with empty key" && return 127 ;;
"."* | "["*) _cortex_io load | jq -rS "${2} | keys[]" ;;
*) _cortex_io load | jq -rS --arg k "${2}" '.[$k] | keys[]' ;;
esac
;;
*) _cortex_io load | jq -cMrS "$(_cortex_jq_path "${@:2}") | keys[]" ;;
esac
;;
"read" | "get" | g*)
case ${#@} in
1) _cortex_io load | jq -cMrS . ;;
2)
case "${2}" in
"") _cortex_logger "$(caller)" ERR "cannot get value with empty key" && return 127 ;;
"."* | "["*) _cortex_io load | jq -cMrS "${2}" ;;
*) _cortex_io load | jq -cMrS --arg k "${2}" '.[$k]' ;;
esac
;;
*) _cortex_io load | jq -rcMS "$(_cortex_jq_path "${@:2}")" ;;
esac
;;
"rm")
case ${#@} in
1) _cortex_logger "$(caller)" ERR "must explicitly delete '.' to reset the entire json" && return 127 ;;
2) cortex set "${2}" null ;;
*) cortex set "$(_cortex_jq_path "${@:2}")" null ;;
esac
;;
"write" | w* | "set" | s*)
read -r -s _dump < <(_cortex_modify_json "${@}")
if [[ -n ${_dump} ]]; then _cortex_io save <<<"${_dump}"; else return $?; fi
;;
"") : ;;
*) _cortex_man && return 127 ;;
esac
}
_cortex_compgen() {
COMPREPLY=()
local cur subcommand i x _x
cur=${COMP_WORDS[COMP_CWORD]}
subcommand=$(cut -d ' ' -f "${COMP_CWORD}" <<<"${COMP_LINE}")
case "${subcommand}" in
"cortex" | "array" | "full") COMPREPLY=($(compgen -W "export find list ls read init get rm write set -e -f" -- "${cur}")) ;;
"-e") COMPREPLY=($(compgen -W "array full" -- "${cur}")) ;;
"-f") COMPREPLY=($(compgen -o plusdirs -f -- "${cur}")) ;;
"export" | "ls" | "list" | "read" | "get" | "rm" | "write" | "set") COMPREPLY=($(compgen -W "$(cortex list 2>/dev/null)" -- "${cur}")) ;;
*)
i=0
while [[ ${i} -le ${COMP_CWORD} ]]; do
i=$((i + 1))
case "${COMP_WORDS[@]: -i:1}" in
"cortex" | "array" | "full" | "-e" | "-f" | "export" | "ls" | "list" | "read" | "get" | "rm" | "write" | "set")
i=$((i - 1))
break
;;
esac
_x=$(cortex list "${COMP_WORDS[@]: -i:i}" 2>/dev/null)
if [[ -n ${_x} ]]; then
COMPREPLY=($(compgen -W "${_x}" -- "${cur}"))
return
fi
done
x="${i}"
while [[ ${x} -ge 1 ]]; do
x=$((x - 1))
_x=$(cortex list "${COMP_WORDS[@]: -i:x}" 2>/dev/null)
if [[ -n ${_x} ]]; then
COMPREPLY=($(compgen -W "${_x}" -- "${cur}"))
return
fi
done
COMPREPLY=($(compgen -W "export find list ls read init get rm write set -e -f" -- "${cur}"))
;;
esac
}
_cortex_setup
if (return 0 2>/dev/null); then
declare -xf _cortex_man _cortex_io _cortex_jq_path _cortex_logger _cortex_modify_json _cortex_setup _cortex_compgen cortex
complete -o filenames -F _cortex_compgen cortex
elif [[ ${#@} -gt 0 ]]; then
cortex "${@}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment