Cmd

Cmd :: # (opaque)

Build and run child processes with native-safe programs, arguments, and environment values.

spawn! : Cmd => Try(Child, [SpawnFailed(IOErr), ..])

Spawn a child process with stdio pipes for bidirectional communication.

child = Cmd.new_str("node").arg_str("repl.js").spawn!()?
child.write_stdin!(Str.to_utf8("1+1\n"))?
response = child.read_stdout!(100)?
child.kill!()?
spawn_grouped! : Cmd => Try(Child, [SpawnFailed(IOErr), ..])

Spawn a child process that gets cleaned up when the parent exits.

Use this for test servers, subprocesses, or anything that shouldn't outlive your program.

**Linux and Windows**: children are guaranteed to die with the parent, even on SIGKILL (via PR_SET_PDEATHSIG / Job Objects). **macOS**: children die on normal exit, Ctrl+C, and crashes, but may survive kill -9 of the parent (kernel limitation).

kill_grouped! : {  } => Try({  }, [KillFailed(IOErr), ..])

Kill all processes spawned via [Cmd.spawn_grouped!] and their children.

This is called automatically on normal program exit, but you can call it explicitly for immediate cleanup.

exec! : OsStr, List(OsStr) => Try({  }, [ExecFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), ..])

Simplest way to execute a command by name with arguments. Stdin, stdout, and stderr are inherited from the parent process.

If you want to capture the output, use [exec_output!] instead.

Cmd.exec!("echo", ["hello world"])?
exec_cmd! : Cmd => Try({  }, [ExecCmdFailed({ command : Str, exit_code : I32 }), FailedToGetExitCode({ command : Str, err : IOErr }), ..])

Execute a Cmd (using the builder pattern). Stdin, stdout, and stderr are inherited from the parent process.

You should prefer using [exec!] instead, only use this if you want to use env, envs or clear_envs. If you want to capture the output, use [exec_output!] instead.

Cmd.new("cargo")
    .arg(["build")
    .env("RUST_BACKTRACE", "1")
    .exec_cmd!()?
exec_output! : Cmd => Try({ stdout_utf8 : Str, stderr_utf8_lossy : Str }, [StdoutContainsInvalidUtf8({ cmd_str : Str, err : [BadUtf8({ problem : _, index : U64 })] }), NonZeroExitCode({ command : Str, exit_code : I32, stdout_utf8_lossy : Str, stderr_utf8_lossy : Str }), FailedToGetExitCode({ command : Str, err : IOErr }), ..])

Execute command and capture stdout and stderr as UTF-8 strings. Invalid UTF-8 sequences are replaced with the Unicode replacement character.

Use [exec_output_bytes!] instead if you want to capture the output in the original form as bytes. [exec_output_bytes!] may also be used for maximum performance, because you may be able to avoid unnecessary UTF-8 conversions.

cmd_output =
    Cmd.new("echo")
        .args(["Hi"])
        .exec_output!()?

Stdout.line!("Echo output: ${cmd_output.stdout_utf8}")?
exec_output_bytes! : Cmd => Try({ stderr_bytes : List(U8), stdout_bytes : List(U8) }, [NonZeroExitCodeB({ exit_code : I32, stdout_bytes : List(U8), stderr_bytes : List(U8) }), FailedToGetExitCodeB(IOErr), ..])

Execute command and capture stdout and stderr in the original form as bytes.

Use [exec_output!] instead if you want to get the output as UTF-8 strings.

cmd_output =
    Cmd.new("echo")
        .args(["Hi"])
        .exec_output_bytes!()?

Stdout.line!("${Str.inspect(cmd_output_bytes)}")? # {stderr_bytes: [], stdout_bytes: [72, 105, 10]}
exec_exit_code! : Cmd => Try(I32, [FailedToGetExitCode({ command : Str, err : IOErr }), ..])

Execute a command and return its exit code. Stdin, stdout, and stderr are inherited from the parent process.

You should prefer using [exec!] or [exec_cmd!] instead, only use this if you want to take a specific action based on a **specific non-zero exit code**. For example, roc check returns exit code 1 if there are errors, and exit code 2 if there are only warnings. So, you could use exec_exit_code! to ignore warnings on roc check.

exit_code = Cmd.new("cat").arg("non_existent.txt").exec_exit_code!()?
new : OsStr -> Cmd

Create a new command with the given program name. Use a function that starts with exec_ to execute it.

cmd = Cmd.new("ls")
new_str : Str -> Cmd

Create a new command from a Roc string.

arg : Cmd, OsStr -> Cmd

Add a single argument to the command. ❗ Shell features like variable subsitition (e.g. $FOO), glob patterns (e.g. *.txt), ... are not available.

cmd = Cmd.new("ls").arg("-l")
arg_str : Cmd, Str -> Cmd

Add a single string argument to the command.

args : Cmd, List(OsStr) -> Cmd

Add multiple arguments to the command. ❗ Shell features like variable subsitition (e.g. $FOO), glob patterns (e.g. *.txt), ... are not available.

cmd = Cmd.new("ls").args(["-l", "-a"])
args_str : Cmd, List(Str) -> Cmd

Add multiple string arguments to the command.

env : Cmd, OsStr, OsStr -> Cmd

Add a single environment variable to the command.

cmd = Cmd.new("env").env("FOO", "bar") # add the environment variable "FOO" with value "bar"
env_str : Cmd, Str, Str -> Cmd

Add a single string environment variable to the command.

envs : Cmd, List((OsStr, OsStr)) -> Cmd

Add multiple environment variables to the command.

cmd = Cmd.new("env").envs([("FOO", "bar"), ("BAZ", "qux")])
envs_str : Cmd, List((Str, Str)) -> Cmd

Add multiple string environment variables to the command.

clear_envs : Cmd -> Cmd

Clear all environment variables before running the command. Only environment variables added via env or envs will be available. Useful if you want a clean command run that does not behave unexpectedly if the user has some env var set.

cmd =
    Cmd.new("env")
        .clear_envs()
        .env("ONLY_THIS", "visible")
to_str : Cmd -> Str

Render a command configuration as a stable, escaped string.

to_inspect : Cmd -> Str

Customize command output for Str.inspect.

Child

Cmd.Child :: # (opaque)

A spawned child process with stdio pipes (see [Cmd.spawn!] and [Cmd.spawn_grouped!]).

**Important**: read_stdout! and read_stderr! block until *exactly* N bytes have been read. If the stream reaches EOF before N bytes are available, the call returns ReadFailed. Use these when you know the exact message size (e.g., length-prefixed protocols) or use wait! to read all output at once.

Call close_stdin! to signal EOF to the child process. Many programs (grep, cat, etc.) wait for stdin EOF before producing output.

Remember to call kill! or wait! when done to clean up resources.

to_inspect : Child -> Str

Render the child without exposing its host handle.

close_stdin! : Child => Try({  }, [CloseFailed(IOErr), ..])

Close the child's stdin, signalling EOF.

kill! : Child => Try({  }, [KillFailed(IOErr), ..])

Kill the child process. For children spawned with [Cmd.spawn_grouped!], this kills the whole process tree.

wait! : Child => Try({ exit_code : I32, stdout : List(U8), stderr : List(U8) }, [WaitFailed(IOErr), ..])

Wait for the child to exit, returning its exit code and any remaining output.

poll! : Child => Try([Exited({ exit_code : I32, stdout : List(U8), stderr : List(U8) }), Running], [PollFailed(IOErr), ..])

Check whether the child has exited, without blocking.

Returns Running if the process is still executing, or Exited({ exit_code, stdout, stderr }) once it has finished. After returning Exited, the process is cleaned up; subsequent calls fail.