# 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 as `result`. - a structured JSON fatal error, set by calling `return_error`, returned as `error`. ### API * `return_result JSON|JO` exits successfully with a JSON result * `return_error JSON|JO [exit_code=1]` exists with exit_code and a JSON error * `args_get [JQ_QUERY=.]` query the arguments using jq ### Utilitary Functions * `ensure_command_exists BINARY` ensures a binary exists, exits with error if not * `os_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 ...` (uses `jo`), * escaped json string: `COMMAND '{"something": true}'` * json wrapped in base64: `COMMAND -b eyJiYW5uZXIiOiJiZWFzdGllIGZvciB0aGUgd2luIn0K` ### Output ```js { "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: ```js {"output": "stdout|stderr", "date":"ISO8061Z", line: "...."} ``` Error: ```js { "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`: ```sh cat commands/ifconfig-interfaces-list | cloyster -s cloyster - <&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): ```sh ./commands/ifconfig-interfaces-names.sh | jq ``` Result: ```json { "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, ... *