cloyster: a shell wrapper for machine-oriented shell scripts
cloyster helps you making small, as portable as possible, single-task focused shell scripts that are primarily consumed by other scripts or machines.
cloyster provides a portable utilitary functions library to help you be efficient transforming and manipulating data.
cloyster scripts are called "commands". A good command does a single thing/action (list something, create an interface, bridge an interface, ...).
Being machine-oriented and automation-oriented, commands accepts arguments as JSON, and return
JSON: this also to easily combine and use commands in commands, filter outputs easily (using jq
),
pipe them together...
Base dependencies: POSIX shell, jq, jo
Scripting commands
A command is a POSIX shell script, using /usr/local/libexec/cloyster
as shebang.
It will run in the cloyster environment and has three outputs:
- the standards outputs, stdout and stderr, which acts as usual: unstructured, line based text, automatically
handled by cloyster and returned as
output
, - the structured JSON result of your command, set by calling
return_result
, returned asresult
. - a structured JSON fatal error, set by calling
return_error
, returned aserror
.
API
return_result JSON|JO
exits successfully with a JSON resultreturn_error JSON|JO [exit_code=1]
exists with exit_code and a JSON errorargs_get [JQ_QUERY=.]
query the arguments using jq
Utilitary Functions
ensure_command_exists BINARY
ensures a binary exists, exits with error if notos_get [KEY=.|jq] [JQ_QUERY]
operating system version (type, name, pretty_name, version, version_id, supported)make_random_identifier [SIZE=32]
date_now_iso_utc
posix_time_microseconds
Arguments/Input
Arguments, or inputs to the command, are also passed in JSON. You have three ways of passing the arguments:
- human friendly:
COMMAND arg1=something ...
(usesjo
), - escaped json string:
COMMAND '{"something": true}'
- json wrapped in base64:
COMMAND -b eyJiYW5uZXIiOiJiZWFzdGllIGZvciB0aGUgd2luIn0K
Output
{
"error": null /* Error */,
"exit_status": 0,
"command": "./commands/ifconfig-interfaces-list.sh",
"arguments": {},
"duration_microseconds": /* microseconds */,
"started_at_utc": /* ISO8061Z */,
"finished_at_utc": /* ISO8061Z */,
"result": /* any */,
"output": [
// Output Line
]
}
Output Line:
{"output": "stdout|stderr", "date":"ISO8061Z", line: "...."}
Error:
{
"type": "execution|command",
"error": "error_name",
// Error specific field
}
Reading script from stdin
You can tell cloyster to execute a script from stdin by setting the -s
or -
option to cloyster
:
cat commands/ifconfig-interfaces-list | cloyster -s
cloyster - <<EOF
json=$(jls --libxo json | jq -r '.["jail-information"].jail | @json')
set_result "${json}"
EOF
Examples
Example: commands/ifconfig-interfaces-names.sh
#!/usr/local/libexec/cloyster
echo "stdout something"
>&2 "stderr something"
set_result $(ifconfig | grep -E "^[a-z0-9_]+:" | cut -d ':' -f 1 | jq -nRr '[inputs | select(length>0)] | @json')
Run it (piped to jq
so you beautify JSON output):
./commands/ifconfig-interfaces-names.sh | jq
Result:
{
"error": false,
"exit_status": 0,
"command": "./commands/ifconfig-interfaces-list.sh",
"arguments": [],
"duration_microseconds": 67,
"started_at_utc": "2022-03-22T09:28:51Z",
"finished_at_utc": "2022-03-22T09:28:51Z",
"result": ["eth0", "eth1", "lo0"],
"output": [
{"output": "stdout", "date":"iso8601 utc", line: "stdout something"},
{"output": "stderr", "date":"iso8601 utc", line: "stderr something"}
]
}
Code guide
meh
- We write POSIX Shell compliant code.
- Use
shellcheck
. - Variables:
- are considered private/local if they begin by a
_
- are considered public if they are in lowercase
- are exported/env if they are in uppercase
- Cloyster itself must be portable (POSIX Shell) and should be able to work on macOS, Linux, ...