System & Process
Goblin provides a small set of natives for interacting with the host system — running shell commands, reading environment variables, and passing data between processes. These are the primitives that make Goblin useful as a build tool and scripting language.
:run_cmd
Runs a shell command and returns a map containing the result.
result | :json_parse(:run_cmd("echo hello")) ok | result["ok"] stdout | result["stdout"]
result | :json_parse(:run_cmd("echo hello")) ok | result["ok"] stdout | result["stdout"]
Signature
:run_cmd(command) :run_cmd(command, cwd)
:run_cmd(command) :run_cmd(command, cwd)
command— the shell command to run as a stringcwd— optional working directory to run the command from
Return value
:run_cmd returns a JSON string1 — use :json_parse to work with it as a map:
| Key | Type | Description |
|---|---|---|
ok |
bool | true if exit code was 0 |
code |
int or nil | exit code from the process |
stdout |
string | everything the process wrote to stdout |
stderr |
string | everything the process wrote to stderr |
Example — capturing output
raw | :run_cmd("deno run build.ts") result | :json_parse(raw) if result["ok"] == false :say("Build failed: " + result["stderr"]) return xx output | result["stdout"]
raw | :run_cmd("deno run build.ts") result | :json_parse(raw) if result["ok"] == false :say("Build failed: " + result["stderr"]) return xx output | result["stdout"]
Example — with working directory
result | :json_parse(:run_cmd("goblin build.gbln", "../site"))
result | :json_parse(:run_cmd("goblin build.gbln", "../site"))
Platform notes
On Windows, the command runs via cmd /C. On Unix, it runs via sh -lc. This means your PATH and shell aliases are available, but the command string should be portable if you intend to run on both platforms.
Error handling
If the process fails to spawn entirely (command not found, bad cwd), Goblin raises a runtime error. If the process runs but exits with a non-zero code, no error is raised — check result["ok"] yourself.
result | :json_parse(:run_cmd("nonexistent-tool")) /// runtime error: process-spawn-failed
result | :json_parse(:run_cmd("nonexistent-tool")) /// runtime error: process-spawn-failed
:env
Reads an environment variable by name. Returns nil if the variable is not set.
port | :env("PORT")
port | :env("PORT")
Signature
:env(name)
:env(name)
Example — with fallback
port | :env("PORT") ?? "4242"
port | :env("PORT") ?? "4242"
Example — reading build context
Goblin sets several environment variables automatically when running scripts via the host. These are available through :env:
query | :trim(:env("GOBLIN_QUERY_STRING")) method | :env("GOBLIN_METHOD") body | :env("GOBLIN_BODY")
query | :trim(:env("GOBLIN_QUERY_STRING")) method | :env("GOBLIN_METHOD") body | :env("GOBLIN_BODY")
| Variable | Description |
|---|---|
GOBLIN_QUERY_STRING |
Query string passed by the host |
GOBLIN_METHOD |
HTTP method (GET, POST, etc.) |
GOBLIN_BODY |
Raw request body |
GOBLIN_NONINTERACTIVE |
Set to 1 when running non-interactively |
:say
Writes a string to stdout followed by a newline. This is Goblin's primary output primitive.
:say("hello world")
:say("hello world")
When a Goblin script is invoked by the host, stdout is captured and returned as the response. This is how Goblin scripts return data — write to stdout with :say.
out | {"ok": true, "result": value} :say(:json_stringify(out))
out | {"ok": true, "result": value} :say(:json_stringify(out))
:input / :ask
Reads a line from stdin. Both names refer to the same native.
name | :input("What is your name? ")
name | :input("What is your name? ")
:input and :ask are only useful in interactive sessions. When Goblin runs non-interactively (e.g. invoked by the host with GOBLIN_NONINTERACTIVE=1), stdin is closed and :input will return nil.
Combining them
A common pattern in Sheriff build scripts is using :run_cmd to invoke external tools, :env to read context passed by the host, and :say to return the result:
query | :trim(:env("GOBLIN_QUERY_STRING")) portal | :split(query, "&")[0] result | :json_parse(:run_cmd("goblin build.gbln", "../site/portals/{portal}")) out | { "ok": result["ok"], "stdout": result["stdout"], "stderr": result["stderr"] } :say(:json_stringify(out))
query | :trim(:env("GOBLIN_QUERY_STRING")) portal | :split(query, "&")[0] result | :json_parse(:run_cmd("goblin build.gbln", "../site/portals/{portal}")) out | { "ok": result["ok"], "stdout": result["stdout"], "stderr": result["stderr"] } :say(:json_stringify(out))
-
:run_cmdreturns a raw JSON string rather than a map directly. This keeps the return type simple and consistent regardless of whether the caller needs to inspect the result. Wrap it in:json_parsewhen you need to access individual fields. ↩