std/process

Function Result
capture(command) a Result[CommandOutput] with the status, out, and err of a finished command
capture_input(command, input) the same, with input as the command's standard input
command_text(command) the standard output without its final line break, or an ERROR with what the command wrote to standard error when it fails
has_program(name) whether a program can be started, as found on PATH

A command is a list, the program and then its arguments, each passed as is: no shell splits or expands them. capture is an ERROR only when the program cannot be started or does not write UTF-8 text; a command that fails is OK, with its status.

import "std/process"

match command_text(["git", "rev-parse", "--short", "HEAD"]) do
    when OK(commit) then
        print("at " + commit)
    when ERROR(message) then
        eprint(message)
end

To run a command while showing its output as it goes, use the built-in run, or start_process to keep working while it runs.

Processes on the event loop

spawn(command, options) starts a program and gives a Process at once. While the loop of std/loop runs, its output arrives in callbacks as the program writes it, and on_exit says when it has ended, once all its output has arrived:

import "std/loop"
import "std/process"

Process build = spawn(["make", "all"], SpawnOptions())?
on_output_lines(build, func(str line) do
    print("make:", line)
end)
on_exit(build, func(int code) do
    print("make ended with", code)
end)
run_loop()

SpawnOptions sets how it starts:

Field Meaning
dir the directory it works in; "" for this program's
env, clear_env variables added to the environment, or with clear_env all of it
output, errors, input each stream: Stdio.PIPE (the default, read or written on the loop), INHERIT (this program's), DISCARD, or FILE(path)
new_group, new_session a process group, or a session, of its own
hide_window no console window on Windows
Function Result
on_output(p, callback), on_errors(p, callback) the bytes as they come, and empty bytes at the end
on_output_lines(p, callback), on_error_lines(p, callback) each line as text, without its line end
on_exit(p, callback) the exit code; on POSIX, 128 and the number of a signal that ended it
write_input(p, data), write_input_text(p, text) queues input, written as the process reads it; gives how many bytes wait
input_pending(p), on_drain(p, callback) the bytes that wait, and a callback when they have all been written
close_input(p) closes the input once what waits is written
kill(p, signal) sends "terminate", "kill", "interrupt", or another signal
kill_tree(p) ends the process and every process it started
finished(p) a Task[int] that settles with the exit code, as on_exit says, in its place
capture_task(command) runs a command on the loop, with no input: a Task[CommandOutput] that settles as capture would return

Pseudo-terminals

spawn_pty(command, options) starts a program on a pseudo-terminal of its own, as it would run in a terminal window, the way a terminal emulator or an editor's terminal panel runs a shell. The program sees a terminal: it can ask for its size, move the cursor, and use colors, and Ctrl+C reaches it as an interrupt. It is a Process like any other, so its output, with the terminal's echo of what it is sent, comes to on_output, what write_input sends is typed into it ("\r" is Enter and "\x03" is Ctrl+C), and on_exit, kill, and kill_tree work as they do for spawn:

PtyOptions options = PtyOptions()
options.columns = 120
options.rows = 40
Process shell = spawn_pty(["bash"], options)?
on_output(shell, func(bytes chunk) do
    write_out_bytes(chunk)
end)
write_input_text(shell, "ls --color\r")

PtyOptions has the terminal's columns and rows (80 and 24), and dir, env, and clear_env as for spawn. On POSIX, TERM is its term ("xterm-256color") and COLORTERM is "truecolor", unless env sets them.

Function Result
spawn_pty(command, options) a Result[Process]
resize_pty(p, columns, rows) gives the terminal a new size, which the program hears of as a window change; says whether the terminal was still there
foreground_name(p) the name of the program in the terminal's foreground, such as the command a shell runs, on Linux and macOS; NONE elsewhere

On POSIX the terminal is a pair from posix_openpt, and the program leads a session of its own with the terminal as its controlling terminal. On Windows it is a pseudo-console (ConPTY), from Windows 10 version 1809; what comes out is what the pseudo-console draws, the text with the sequences that place and color it, rather than the bytes the program wrote.

A bare program name is looked up on PATH, and the built-in find_program gives the path it finds. A program that ends without reading its input drops the rest of it. On Windows each process runs in a job object, so kill_tree reaches every process it started; on POSIX it needs the process started with new_group or new_session, and ends its group. A process's pid is the id the system gives it.