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

Environment (std:env)

Read and write environment variables, and reach the script’s command-line arguments.

Overview

std:env exports three values — not functions:

ExportKindDescription
varMap-like objectThe live process environment
argsMapCommand-line arguments, keyed by "0", "1", …
argvMapThe same snapshot as args

Because these are values, they are used without call parentheses: env:var:HOME, env:args["0"]. There is no env:get(...) and no env:args().

Quick Start

import std:env
import std:println

println(env:var::contains("PATH"))            # true
println(env:var::get("NO_SUCH_VAR", "none"))  # none

env:var["APP_MODE"] = "debug"
println(env:var:APP_MODE)                     # debug

var — The Process Environment

env:var behaves like a map of strings to strings, and it is live: assigning to it changes the environment of the running process, so child processes started with backticks see the new value.

Reading

import std:env
import std:println

home = env:var:HOME          # bare-identifier key
path = env:var["PATH"]       # any string expression as key

println(home::is_string())   # true
println(path::is_string())   # true

A missing variable raises Key not found: Environment variable not found, which terminates the program. Use get with a default, or contains, for anything that may be absent:

import std:env
import std:println

port = env:var::get("DEMO_PORT", "8080")
println(port)  # 8080

println(env:var::contains("DEMO_PORT"))  # false

Writing

import std:env
import std:println

env:var["GREETING"] = "hello"
println(`echo $GREETING`)  # hello

merge sets several variables at once, and delete removes one:

import std:env
import std:println

env:var::merge({"SERVICE": "api", "REGION": "eu-west-1"})
println(env:var:SERVICE)             # api

println(env:var::delete("SERVICE"))  # true
println(env:var::contains("SERVICE"))  # false

Methods

env:var supports the map methods:

MethodReturnsDescription
get(name, default = nil)String or defaultSafe read
contains(name)BooleanWhether the variable is set
keys()ListAll variable names
values()ListAll variable values
to_list()List(name, value) tuples
length()NumberNumber of variables
delete(name)BooleanUnset a variable
merge(map)nilSet every pair in map
import std:env
import std:println

env:var::merge({"DEMO_ONE": "1", "DEMO_TWO": "2"})

demo = env:var::keys()::filter(|k| k::starts_with("DEMO_"))
println(demo::sort())  # [DEMO_ONE, DEMO_TWO]

args and argv — Command-Line Arguments

env:args and env:argv are maps captured at startup and keyed by strings, so index them with "0", "1", … and never with the numbers 0, 1. Interpreter flags (anything starting with -) are consumed by the CLI and never appear here.

import std:env
import std:println

println(env:args::is_map())                    # true
println(env:args::get("0", nil)::is_string())  # true

The intended layout is "0" for the script path and "1", "2", … for the script’s own arguments.

Known bug in 0.1.22: positional arguments do not work. Every argument is written back to key "0", so the map always holds exactly one entry: the script path when the script is run with no arguments, and otherwise the last argument. Keys "1" and up are never populated.

suji script.si            # env:args is {0: script.si}
suji script.si build      # env:args is {0: build}
suji script.si build fast # env:args is {0: fast}

Until this is fixed, treat env:args as unreliable for anything but a single value, read it defensively, and pass multiple inputs another way.

import std:env
import std:println

println(env:args::length())        # 1
println(env:args::contains("1"))   # false

Read the value defensively and do not rely on higher indices:

import std:env
import std:println

first = env:args::get("1", nil)
println(first == nil)  # true

For scripts that need several inputs, prefer environment variables or a single argument that you split yourself:

import std:env
import std:println

# TARGETS="a,b,c" suji script.si
raw = env:var::get("TARGETS", "")
targets = match {
    raw == "" => [],
    _ => raw::split(","),
}

println(targets::length())  # 0

Gotchas

  • env:var, env:args and env:argv are values; calling them (env:var()) is an error.
  • Environment values are always strings — convert with ::to_number() when you need arithmetic, and validate first because an unparsable string is a runtime error.
  • Reading a missing variable with : or [] terminates the program; get is the safe form.
  • args and argv are snapshots taken at startup, so mutating them has no effect on anything else.

See Also