defmodule ExCmd do @moduledoc """ ExCmd is an Elixir library to run and communicate with external programs with back-pressure. """ @doc """ Runs the given command with arguments and return an Enumerable to read command output. First parameter must be a list containing command with arguments. example: `["cat", "file.txt"]`. ### Optional * `input` - Input can be binary, iodata, `Enumerable` (list, stream) or a function which accepts `Collectable`. * Binary ``` # binary ExCmd.stream!(~w(base64), input: "Hello Wolrd") ``` * Enumerable or iodata ``` # list ExCmd.stream!(~w(base64), input: ["hello", "world"]) # iodata ExCmd.stream!(~w(cat), input: ["He", ["llo"], [' ', ?W], ['or', "ld"]]) # stream ExCmd.stream!(~w(cat), input: File.stream!("log.txt", [], 65536)) ``` * Collectable: If the input in a function with arity 1, ExCmd will call that function with a `Collectable` as the argument. The function must *push* input to this collectable. Return value of the function is ignored. ``` ExCmd.stream!(~w(cat), input: fn sink -> Enum.into(1..100, sink, &to_string/1) end) ``` * `exit_timeout` - Time to wait for external program to exit. After writing all of the input we close the stdin stream and wait for external program to finish and exit. If program does not exit within `exit_timeout` an error will be raised and subsequently program will be killed in background. Defaults timeout is `:infinity`. All other options are passed to `ExCmd.Process.start_link/2` ## Examples ``` ExCmd.stream!(~w(ffmpeg -i pipe:0 -f mp3 pipe:1), input: File.stream!("music_video.mkv", [], 65336)) |> Stream.into(File.stream!("music.mp3")) |> Stream.run() ``` With input as binary ``` iex> ExCmd.stream!(~w(cat), input: "Hello World") ...> |> Enum.into("") "Hello World" ``` ``` iex> ExCmd.stream!(~w(base64), input: <<1, 2, 3, 4, 5>>) ...> |> Enum.into("") "AQIDBAU=\n" ``` With input as list ``` iex> ExCmd.stream!(~w(cat), input: ["Hello ", "World"]) ...> |> Enum.into("") "Hello World" ``` With input as iodata ``` iex> ExCmd.stream!(~w(base64), input: [<<1, 2,>>, [3], [<<4, 5>>]]) ...> |> Enum.into("") "AQIDBAU=\n" ``` When program exit abnormally it will raise `ExCmd.Stream.AbnormalExit` error with exit_status. ``` iex> try do ...> ExCmd.stream!(["sh", "-c", "exit 5"]) |> Enum.to_list() ...> rescue ...> e in ExCmd.Stream.AbnormalExit -> ...> e.exit_status ...> end 5 ``` """ @type collectable_func() :: (Collectable.t() -> any()) @spec stream!(nonempty_list(String.t()), input: Enum.t() | collectable_func(), exit_timeout: timeout(), cd: String.t(), env: [{String.t(), String.t()}], log: boolean() ) :: ExCmd.Stream.t() def stream!(cmd_with_args, opts \\ []) do ExCmd.Stream.__build__(cmd_with_args, opts) end end