Docs/Files & system

Processes

Run other programs and read their output.

On this page

core.Process starts other programs, and core.exec runs a shell command. Waiting for a child or its output only pauses the current coroutine, so a server on the same thread keeps running.

Run a program and collect its output#

Valk
use valk.core

fn main() {
    let result = core.Process.output("echo", .{ "hello", "world" }) ! {
        println("Could not start echo")
        return
    }
    println(result.code)   // 0
    print(result.stdout)   // hello world
}

output waits until the program ends and returns its exit code, stdout and stderr. Each argument reaches the program as it is, with no shell and no quoting. Named arguments set the rest:

Argument Meaning
input Text written to the child's stdin
cwd The working directory of the child
env The child's environment, replacing the current one
Valk
use valk.core

fn main() {
    let sorted = core.Process.output("sort", input: "pear\napple\n") ! return
    print(sorted.stdout) // apple, pear
}

Starting fails with an io.IoError when the program cannot be found. A program that runs but fails is not an error: check code.

Run a shell command#

core.exec runs a command line through /bin/sh (cmd.exe on Windows) and returns the exit code and the output, stderr included:

Valk
use valk.core

fn main() {
    let code, out = core.exec("echo one; echo two | tr a-z A-Z")
    println(code) // 0
    print(out)    // one, TWO
}

The shell reads quotes, spaces and ; in the command, so never put outside input in it. Use Process.output with arguments instead.

Start a program and keep control#

Process.start returns right away. Each of stdin, stdout and stderr is .inherit (the default), .pipe or .discard:

Valk
use valk.core

fn main() {
    let child = core.Process.start("sort", stdin: .pipe, stdout: .pipe) ! return
    child.stdin.write("b\na\n") ! return
    child.stdin.close() ! return // ends the child's input
    print(child.stdout.read_all() ! return) // a, b
    println(child.wait() ! return)          // 0
}
Method Does
wait(timeout_ms) Waits for the exit code; throws timeout when the time runs out
did_exit() Whether it has exited, without waiting
signal(.terminate) Sends a signal (on Windows only .terminate)
stop() Kills the child and waits for it
detach() Lets the child run on without you
id() The process id

The pipes are an io.Reader / io.Writer, so io.copy and io.LineReader work on them. Dropping a Process does not stop the child.

Environment variables#

Valk
use valk.core

fn main() {
    core.setenv("APP_MODE", "test") ! return
    println(core.getenv("APP_MODE") !? "none") // test
    core.unsetenv("APP_MODE") ! return
    println(core.getenv("APP_MODE") !? "none") // none
    println(core.env_vars().length > 0)        // true
}

getenv throws when the variable is not set. Changes apply to this process and the children it starts afterwards. To give one child its own environment, change a copy from core.env_vars() and pass it as env.

Arguments and exit code#

main can take the command-line arguments, and core.exit ends the program with a code:

Valk
use valk.core

fn main(args: Array[String]) {
    if args.length < 2 {
        println("usage: greet <name>")
        core.exit(1)
    }
    println("Hello " + args[1])
}

args[0] is the program itself. main may also return an i32 as the exit code.

About the machine#

Function Returns
core.cpu_thread_count() Logical CPUs
core.cpu_core_count() Physical cores
core.memory_limit() RAM, or the container's limit
core.process_id() This process's id
core.hostname() The machine's name