Safe OS process execution for Elixir.
Combines NIF-based async I/O with a persistent shepherd binary to guarantee zero zombie processes, even under BEAM SIGKILL.
Quick start
# Simple command execution
{output, 0} = NetRunner.run(~w(echo hello))
# Streaming with input
NetRunner.stream!(~w(cat), input: "hello world")
|> Enum.to_list()
# => ["hello world"]
# Piping data through a command
NetRunner.stream!(~w(wc -c), input: "hello")
|> Enum.join()
# => " 5\n"
Summary
Functions
@spec run( NetRunner.Command.t() | [String.t()], keyword() ) :: {binary(), non_neg_integer()} | {:error, term()}
Runs a command and collects all output.
Accepts either a command list [executable | args] or a %NetRunner.Command{} struct.
Returns {output, exit_status} where output is the concatenated stdout.
Options
:stderr-:consume(default, drained internally so the child never blocks on a full stderr pipe) or:disabled:input- data to write to stdin (binary or enumerable):timeout- maximum wall-clock time in milliseconds. Sends SIGTERM then SIGKILL on timeout. Returns{:error, :timeout}instead of{output, exit_status}.:max_output_size- maximum bytes to collect from stdout. Kills the process and returns{:error, {:max_output_exceeded, partial_output}}if exceeded.
Examples
{output, 0} = NetRunner.run(~w(echo hello))
{"hello\n", 0} = {output, 0}
{output, 0} = NetRunner.run(~w(cat), input: "from stdin")
{:error, :timeout} = NetRunner.run(~w(sleep 100), timeout: 100)
{:error, {:max_output_exceeded, _partial}} =
NetRunner.run(["sh", "-c", "yes"], max_output_size: 1000)
# With a Command struct:
cmd = NetRunner.Command.new("echo", ["hello"], timeout: 5_000)
{output, 0} = NetRunner.run(cmd)
@spec stream( NetRunner.Command.t() | [String.t()], keyword() ) :: {:ok, Enumerable.t()} | {:error, term()}
Like stream!/2 but returns {:ok, stream} or {:error, reason}.
Accepts either a command list [executable | args] or a %NetRunner.Command{} struct.
@spec stream!( NetRunner.Command.t() | [String.t()], keyword() ) :: Enumerable.t()
Creates a stream for incremental I/O with the command.
Accepts either a command list [executable | args] or a %NetRunner.Command{} struct.
Returns a Stream that yields stdout binary chunks.
Raises on process start failure.
Options
:input- data to write to stdin (binary, list, or Stream):stderr-:consume(default) or:disabled
Examples
# Stream through a command
NetRunner.stream!(~w(sort))
|> Enum.to_list()
# With input
NetRunner.stream!(~w(tr a-z A-Z), input: "hello")
|> Enum.join()
# => "HELLO"
# With a Command struct:
cmd = NetRunner.Command.new("cat", [], input: "hello")
NetRunner.stream!(cmd) |> Enum.to_list()