Rambo: Run your command. Send EOF. Get output

After wrestling with ports for the nth time, I wrote Rambo to easily get output from external programs.

The idea to use a shim is far from original, but previous libraries were either too heavyweight or complicated (comparisons here). Rambo is intentionally kept simple and lightweight, the shim is written in Rust.


Rambo has one mission. Start your program, pipe standard input, send EOF and return with output.

Rambo.run("cat", in: "hello")
{:ok, %Rambo{out: "hello"}}

Rambo.run("echo", ["-n", "world"])
{:ok, %Rambo{out: "world"}}

Rambo.run("ls") |> Rambo.run("sort") |> Rambo.run("head")
{:ok, %Rambo{out: "bar\nbaz\nfoo\n"}}



How is this different from Porcelain?

Answered in the README:


Oh nice! From the readme:


To make Porcelain useful, you must install its shim Goon separately. Rambo ships with the required native binaries.

Goon is written in Go, a multi-threaded runtime with a garbage collector. To be as lightweight as possible, Rambo’s shim is written in Rust. Single threaded, no garbage collector, no runtime overhead.

@AndyL Glad you found it, but the most important reason was obscured by my poor attempt at whimsy. I’ve updated the Porcelain section with

Most importantly, Goon currently leaks processes. This isn’t an intractable problem, in fact writing a new driver to replace Goon should fix it. But Porcelain appears to be abandoned and I need this in production so effort went into creating Rambo.


Rambo 0.3.0 released

The shim is now fully powered by asynchronous I/O! Subtle race conditions and deadlocks around threads are gone. Also, you can now set a timeout for your command.



Great lib, but it is sad to accept one of the motivations to do it:


Erlang ports do not work with programs that expect EOF to produce output. The only way to close standard input is to close the port, which also closes standard output, preventing results from coming back to your app. This gotcha is marked Won’t Fix.

I think that we at Erlang and Elixir community can do better than this, adressing this issue root cause.

cc: @garazdawi @josevalim

PS: I cannot sell Erlang to a friend after he tells me that cannot use it without some half baked solution, at the time Porcelain was already abandoned.


But why did they mark it Won’t Fix? Makes me wonder if there is a deep and potentially intractable reason