Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

exec

Import exec to configure and execute external commands. Commands are invoked directly: arguments are never parsed by a shell.

import "exec"

var cmd = exec.Command(
    "git",
    ["status", "--short"],
    stdout=exec.CAPTURE,
    stderr=exec.CAPTURE
)

var result = cmd.run()
if result.success {
    print(result.stdout.decode())
}

Command

Command(name, args=[], cwd=unit, env=unit,
        stdin=INHERIT, stdout=INHERIT, stderr=INHERIT)

name and every element of args must be strings. cwd accepts unit, a string, or a Path. An omitted env inherits the current process environment; a dictionary replaces it completely, so env={} starts the command with an empty environment. Environment keys and values must be strings.

The standard-stream policies are:

PolicyMeaning
INHERITUse the corresponding Goblin process stream
DISCARDProvide EOF for stdin or discard output
CAPTURECapture stdout or stderr into the result

stdin also accepts Str or Bytes. CAPTURE is valid only for stdout and stderr. Captured values are Bytes, because command output is not necessarily UTF-8; call decode() when text is expected.

Besides the policies, stdout and stderr accept any writer object — an object with a write(data) method, such as an open fs file. The command's output streams into it as Bytes chunks, so large output never has to be captured in memory:

import "exec"
import "fs"

var log = fs.create("build.log")
exec.Command("make", stdout=log, stderr=log).run()
log.close()

exec never calls the writer's close(); close the target yourself when the command finishes. With start(), chunks may arrive while your program is doing other work, so do not write to the same object from Goblin code until wait() returns.

Executing a command

cmd.run() starts, waits for, and reaps a command. Output behavior comes only from the stream configuration on Command.

var result = exec.Command(
    "gofmt",
    stdin="package main\nfunc main(){}",
    stdout=exec.CAPTURE,
    stderr=exec.CAPTURE
).run()

For explicit asynchronous control, use start() followed by wait():

var cmd = exec.Command("worker", ["--once"])
cmd.start()
print(cmd.pid)
var result = cmd.wait()

A command can be started only once. wait() before start() and a second execution attempt raise ValueError. Repeated calls to wait() return the same cached result. kill() terminates a started command; call wait() afterward to obtain its result. cmd.pid is unit before startup, and cmd.running() reports whether the command has not yet been reaped.

Result

AttributeTypeMeaning
codeIntExit code; a signal may produce -1
successBoolWhether the exit code is zero
stdoutBytes or unitCaptured stdout, if configured
stderrBytes or unitCaptured stderr, if configured

A non-zero exit code is a normal result, not an exception. Failures to start or wait for the command raise an I/O-related error. Inspect code or success and implement any command-specific failure policy in Goblin code.

Shell commands

exec does not interpret pipes, redirects, glob patterns, or shell variables. Pass every argument as a separate list element. If shell syntax is explicitly required, invoke a platform shell yourself, for example exec.Command("sh", ["-c", script]); do not insert untrusted text into such a script.