Skip to content

Command Literals and Safety

On this page

The sh"…" literal builds a lazy Cmd; it does not start a shell or execute a process. Call run() for a checked Result<Output, CmdError> or run_raw() if a non-zero exit status is data rather than an error. These examples require the local Zolo CLI, so the browser runner is disabled.

Builds a command, runs it, supplies standard input, and inspects an unchecked non-zero result.

01-command-literals.zolo
// Feature: sh"…" command literals — Zolo as a typed Bash replacement.

// Syntax: `sh"git status {branch}"` builds a lazy `Cmd` (nothing runs);

// `.run()` executes and returns `Result<Output, CmdError>`.

// When to use: any place you'd reach for a shell script — with types,

// Result-based errors, and injection-safe interpolation for free.


// Lazy: building a command executes nothing.

let cmd = sh"git version"
print("built: {cmd}")

// .run() blocks and returns Result<Output, CmdError> — status ≠ 0 is Err.

match cmd.run() {
  .Ok(out) => print("exit {out.status}: {out.stdout.trim()}"),
  .Err(e) => print("failed [{e.status}]: {e.stderr}"),
}

// Chainable builders: cwd / env / stdin / timeout — each returns a new Cmd.

let hashed = sh"git hash-object --stdin".stdin("hello zolo").run()
match hashed {
  .Ok(out) => print("hash: {out.stdout.trim()}"),
  .Err(e) => print("failed: {e}"),
}

// .run_raw() never raises on status ≠ 0 — the `check=False` escape hatch.

let raw = sh"git definitely-not-a-subcommand".run_raw()
print("run_raw status (non-zero, no Err): {raw.status != 0}")

Requires the Zolo CLI/host — open in the playground or run locally.

Every interpolated value becomes one opaque argv entry. Arrays expand one element at a time, and Raw is the explicit escape hatch for intentional word-splitting.

Shows that hostile text and filenames containing spaces remain single arguments.

02-safe-interpolation.zolo
// Feature: safe interpolation — the shell-injection class, deleted.

// Syntax: every `{expr}` in sh"…" becomes ONE opaque argv word, never

// re-interpreted by a shell (there is no shell: argv goes straight to the

// OS). `Raw(text)` is the explicit opt-out; arrays splice element-wise.

// When to use: always — it's the default. Compare with the legacy

// `os.execute("cat " + filename)`, where concatenation IS the bug.


// A hostile value stays ONE argument — argv inspection proves it:

let evil = "; rm -rf ~ --oops"
let c = sh"cat {evil}"
let argv = c.__stages[0].argv
print("argv len (cat + 1 opaque arg): {argv.len()}")
print("the whole payload is one word: {argv[1]}")

// Values glue to adjacent literal text and still form one word:

let rev = "deadbeef"
let tagged = sh"docker build -t app:{rev} ."
print("glued word: {tagged.__stages[0].argv[3]}")

// Arrays splice as separate arguments (element boundaries, not spaces):

let files = ["a.txt", "name with spaces.txt"]
let tar = sh"tar -cf out.tar {files}"
print("spaced filename survives as one word: {tar.__stages[0].argv[4]}")

// Raw() — the author takes responsibility; text is word-split like source:

let flags = Raw("-la --color=auto")
let ls = sh"ls {flags}"
print("raw splice words: {ls.__stages[0].argv.len() - 1}")

Requires the Zolo CLI/host — open in the playground or run locally.

run() uses the ordinary Result type, so multi-step scripts can propagate the first CmdError with ?.

Propagates command failures and distinguishes a missing executable with status -1.

03-typed-result.zolo
// Feature: Cmd.run() → Result<Output, CmdError> — errors you can type.

// Syntax: status ≠ 0 is already Err; `?` propagates it like any Result.

// Launch failure (missing binary) and timeout use status -1.

// When to use: multi-step scripts where Bash would scatter `$?` checks.


fn describe() -> Result<str, CmdError> {
    // Each step propagates with `?` — first failure short-circuits.

    // (Bind before the field access: `run()?.stdout` would lex as the

    // `?.` optional-chain operator, which is a different thing.)

    let v = sh"git version".run()?
    let version = v.stdout.trim()
    let h = sh"git hash-object --stdin".stdin(version).run()?
    return Result.Ok("{version} -> {h.stdout.trim()}")
}

match describe() {
    Result::Ok(line) => print(line),
    Result::Err(e) => print("step failed [{e.status}]: {e.stderr}"),
}

// Missing binary → Err with status -1 (never a shell "command not found"):

match sh"zolo-example-missing-binary".run() {
    Result::Ok(_) => print("unexpected"),
    Result::Err(e) => print("launch failure status: {e.status}"),
}

Requires the Zolo CLI/host — open in the playground or run locally.

Search Zolo

9 results

enespt-br