Executes shell commands via a Port and returns captured output.
Each invocation spawns a new shell process, captures merged stdout/stderr, and returns the output alongside the exit code. No state carries over between calls.
Runner.run("echo hello", {"/bin/bash", ["-c"]}, dir: "/tmp")
#=> {:ok, %{output: "hello\n", exit_code: 0}}
Runner.run("exit 1", {"/bin/bash", ["-c"]}, dir: "/tmp")
#=> {:error, :nonzero, %{output: "", exit_code: 1}}The runner executes arbitrary shell commands with full system access. It is not a security boundary — OS-level sandboxing (containers, restricted users) is the caller's responsibility.
Options
:dir(required) — working directory for the command:env— extra environment variables as[{String.t(), String.t()}], merged additively with the inherited environment. Default[]:timeout— execution timeout in milliseconds. Default30_000:max_output— output truncation limit in bytes. Tail-biased, snapped to line boundaries. Default50_000:command_prefix— string prepended to every command. Defaultnil
Return values
{:ok, %{output: "hello\n", exit_code: 0}}
{:error, :nonzero, %{output: "error msg\n", exit_code: 1}}
{:error, :timeout, %{output: "partial..."}}On success, exit_code is always 0. On a non-zero exit, the output
captured up to that point is included. On timeout, partial output
collected before the deadline is returned.
Summary
Functions
Resolves the shell to use for command execution.
Executes command in the given shell and returns captured output.
Types
@type result() :: {:ok, %{output: String.t(), exit_code: 0}} | {:error, :nonzero, %{output: String.t(), exit_code: pos_integer()}} | {:error, :timeout, %{output: String.t()}}
Result of a command execution — success, non-zero exit, or timeout.
Functions
Resolves the shell to use for command execution.
Checks in order: explicit :shell option, /bin/bash, /bin/sh.
Returns a {executable, args} tuple suitable for Port.open/2.
Raises ArgumentError if no usable shell is found or the option is invalid.
Runner.resolve_shell([])
#=> {"/bin/bash", ["-c"]}
Runner.resolve_shell(shell: {"/bin/zsh", ["-c"]})
#=> {"/bin/zsh", ["-c"]}
Executes command in the given shell and returns captured output.
The shell argument is a {executable, args} tuple — the command string
is appended to args when spawning the port.