Literais de Comando e Segurança
Nesta página
O literal sh"…" monta um Cmd lazy; ele não inicia um shell nem executa um
processo. Chame run() para obter um Result<Output, CmdError> verificado ou
run_raw() quando um status diferente de zero for apenas um dado. Estes
exemplos exigem a CLI local do Zolo, por isso o runner do navegador fica
desativado.
Monta e executa um comando, fornece a entrada padrão e inspeciona um resultado não verificado diferente de zero.
// 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}")
Requer a CLI/host do Zolo — abra no playground ou rode localmente.
Cada valor interpolado vira uma única entrada opaca de argv. Arrays expandem um
elemento por vez, e Raw é a saída explícita para divisão intencional em
palavras.
Mostra que texto hostil e nomes de arquivo com espaços continuam sendo argumentos únicos.
// 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}")
Requer a CLI/host do Zolo — abra no playground ou rode localmente.
run() usa o tipo Result normal, então scripts com várias etapas podem
propagar o primeiro CmdError com ?.
Propaga falhas de comandos e identifica um executável ausente com status -1.
// 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}"),
}
Requer a CLI/host do Zolo — abra no playground ou rode localmente.
Veja também