std.os

Process-level helpers that live directly on std (not under a submodule) —
system, exit, chdir, pid, and the arg_parser factory. Call them
with dot, like any module function. There is no std.os object — the std.os title is just this
page’s name (call std.system(...), not std.os.system(...)). See
Overview. For the working directory use
std.path.cwd() (the old std.cwd alias was removed).

Reference

std.system(command)   # run a shell command -> {output, status}
std.exit([code])      # terminate the process
std.chdir(path)       # change cwd (returns nil)
std.pid()             # current process ID (long)

std.system — run a command and capture its stdout

std.system(command) runs the command through the shell and returns an
object with two fields:

  • output — a string of everything the command wrote to stdout.
  • status — the process exit code (0 on success, non-zero on
    failure like 1 or 127).
var r = std.system("whoami")
print(r.status)     # 0        (success)
print(r.output)     # cory     (what the command printed)

var date = std.system("date +%Y-%m-%d")
print("today is", date.output)   # today is 2026-08-31

# capture multi-line output as a single string (with embedded newlines)
var ls = std.system("ls /tmp")
print(ls.output)      # one file path per line

Captured output typically ends with a trailing newline, so when you embed
it in another string you usually want to trim it:

var who = std.system("whoami").output
var name = std.string.trim(who)          # drop the trailing newline
print("hello, " + name + "!")

Branch on the exit code:

var check = std.system("test -f /etc/hostname")
if (check.status == 0) {
  print("file exists")
} else {
  print("file missing")
}

print(std.system("exit 3").status)     # 3 — the command's own exit code
stdout

std.system captures stdout; any stderr the command writes goes
straight to the script’s stderr and is not included in output. Redirect
inside the command if you need it, e.g. std.system("cmd 2>&1").

The

std.system(command) hands the whole string to the system shell (/bin/sh -c on POSIX, cmd /c on Windows) — it does not exec the program
directly. Shell metacharacters are live: ;, &&, |, $VAR, backquotes,
and globs all expand or chain. Never build the command from untrusted input
(user text, file names, network data) without sanitizing — that is a
command-injection hole:

# DANGEROUS if user_name comes from outside the script:
std.system("greet " + user_name)   # user_name = "x; rm -rf ~" runs both

Pass only trusted, hardcoded commands, or strip/quote metacharacters
yourself. Guarded engines deny std.system entirely unless constructed with
allow_subprocess.

std.path.cwd and std.chdir — the working directory

std.path.cwd() returns the current directory as a string; std.chdir(path)
changes it (returning nil). They work together:

print(std.path.cwd())       # e.g. /home/user/projects
std.chdir("/tmp")
print(std.path.cwd())       # /tmp   — the process cwd has changed
std.chdir("..")             # relative paths work too

This matters because file operations and relative paths resolve against the
process working directory. std.chdir(path) is the only way to change the
directory.

See also std.path (std.path.cwd) and std.file.

std.pid — the process id

std.pid() returns the current process ID as a long (unique only among
currently-running processes):

print(std.pid())                 # e.g. 95516

var tag = "proc-" + std.pid()
std.file.write("/tmp/" + tag + ".lock", "owned by " + std.pid())
print("announcing:", tag)

Run the same script twice and each writes its own file:

instance pid = 99716     my lock file: /tmp/proc-99716.lock
instance pid = 99735     my lock file: /tmp/proc-99735.lock

The pid is useful for prefixed log lines and for pid-named lock files.

std.arg_parser — command-line parsing

Scripts receive raw argv via std.env.args(), and std.arg_parser
turns it into a validated options object — flags, valued options, and
positionals with defaults, type coercion, and generated --help:

var p = std.arg_parser("greet", "greet someone, optionally loudly")
p.add_flag("loud", "l", "use capitals")
p.add_option("greeting", "g", "greeting word", "hello")
p.add_option("repeat", "n", "times to print", 1)
p.add_positional("name", "who to greet")

var args = p.parse(std.env.args())
# scrii_repl greet.scr --loud -n 3 Bob
# -> {loud: true, greeting: "hello", repeat: 3, name: "Bob", _help: false}
for (i : std.seq.range(args.repeat)) {
  var line = args.greeting + ", " + args.name
  if (args.loud) { line:upper() }
  print(line)
}
Builder Purpose
arg_parser([prog[, description]]) Create a parser (used in usage text)
add_flag(name[, short[, description]]) Boolean --name / -s, plus --no-name to negate and --name=true/false
add_option(name[, short[, description[, default[, choices]]]]) Valued --name v, --name=v, -s v, -sV; a numeric default coerces the value to its type; choices restricts it
add_positional(name[, description[, default]]) Positional in declaration order; required unless a default is given
parse(argv) Parse an array (usually std.env.args()) → {...values, _help}
help() The generated usage text as a string

Rules: combined shorts (-abc), -- ends option parsing (the rest is positional), unknown options / missing values / missing required or extra positionals throw INVALID_ARGUMENT naming the problem; a bad numeric coercion throws INVALID_CONVERSION. --help/-h print usage and return with _help set (check it, then std.exit(0) or stop) — unless you define your own help/h flag, which takes precedence.

Pure compute, so arg_parser stays available in guarded engines.

See also

  • std.file — file I/O relative to cwd
  • std.path — join/resolve with cwd
  • std.env — environment that system inherits