diff --git a/README.md b/README.md index 84f92aa..9876e96 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ raise freely. ## Use -Just add `{:sshex, "1.0.0"}` to your deps on `mix.exs`. +Just add `{:sshex, "1.1.0"}` to your deps on `mix.exs`. Then assuming `:ssh` application is already started (hence it is listed on deps), you should acquire an SSH connection using `:ssh.connect/4` like this: @@ -40,6 +40,14 @@ status code too, you can use `run/4` like this: {:ok, res, 0} = SSHEx.run conn, 'ls /some/path' ``` +You can pass the option `:separate_streams` to get separated stdout and stderr. +Like this: + +```elixir + {:ok, stdout, stderr, 2} = SSHEx.run conn, 'ls /nonexisting/path', + 5000, 5000, [separate_streams: true] +``` + You will be reusing the same SSH connection all over. @@ -52,5 +60,16 @@ the `:user_dir` option for `:ssh.connect/4`. See ## TODOs +* Put every optional parameter under a unique Keyword list * Add testing using [meck](https://github.com/eproxus/meck) * Add tunnelling helpers [*](http://erlang.org/pipermail/erlang-questions/2014-June/079481.html) + +## Changelog + +### 1.1 + +* Add support for separate stdout/stderr responses. + +### 1.0 + +* Initial release diff --git a/lib/sshex.ex b/lib/sshex.ex index ce60a97..106cbbf 100644 --- a/lib/sshex.ex +++ b/lib/sshex.ex @@ -18,25 +18,33 @@ defmodule SSHEx do Any failure related with the SSH connection itself is raised without mercy. Returns `{:ok,data,status}` on success. Otherwise `{:error, details}`. + + If `:separate_streams` is `true` then the response on success looks like `{:ok,stdout,stderr,status}`. + + TODO: For 2.0 release, join every optional argument into one big opts list """ - def run(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000) do + def run(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000, opts \\ []) do conn |> open_channel(channel_timeout) |> exec(conn, cmd, exec_timeout) - |> get_response(exec_timeout) + |> get_response(exec_timeout, "", "", nil, false, opts) end @doc """ - Convenience function to run `run/4` and get output string straight from it, + Convenience function to run `run/5` and get output string straight from it, like `:os.cmd/1`. - Returns `response` only if `run/4` return value matches `{:ok, response, _}`. - Raises any `{:error, details}` returned by `run/4`. Note return status from + Returns `response` only if `run/5` return value matches `{:ok, response, _}`, + or returns `{stdout, stderr}` if `run/5` returns `{:ok, stdout, stderr, _}`. + Raises any `{:error, details}` returned by `run/5`. Note return status from `cmd` is ignored. + + TODO: For 2.0 release, join every optional argument into one big opts list """ - def cmd!(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000) do - case run(conn, cmd, channel_timeout, exec_timeout) do + def cmd!(conn, cmd, channel_timeout \\ 5000, exec_timeout \\ 5000, opts \\ []) do + case run(conn, cmd, channel_timeout, exec_timeout, opts) do {:ok, response, _} -> response + {:ok, stdout, stderr, _} -> {stdout, stderr} any -> raise inspect(any) end end @@ -63,18 +71,20 @@ defmodule SSHEx do # Loop until all data is received. Return read data and the exit_status. # - defp get_response(channel, timeout, stdout \\ "", stderr \\ "", status \\ nil, closed \\ false) do + # TODO: For 2.0 release, join every optional argument into one big opts list + # + defp get_response(channel, timeout, stdout \\ "", stderr \\ "", status \\ nil, closed \\ false, opts \\ []) do # if we got status and closed, then we are done parsed = case {status, closed} do - {st, true} when not is_nil(st) -> {:ok, stdout, stderr, status} + {st, true} when not is_nil(st) -> format_response({:ok, stdout, stderr, status}, opts) _ -> receive_and_parse_response(channel, timeout, stdout, stderr, status, closed) end # tail recursion case parsed do {:loop, {channel, timeout, stdout, stderr, status, closed}} -> # loop again, still things missing - get_response(channel, timeout, stdout, stderr, status, closed) + get_response(channel, timeout, stdout, stderr, status, closed, opts) x -> x end end @@ -98,4 +108,13 @@ defmodule SSHEx do end end + # Format response for given raw response and given options + defp format_response(raw, opts) do + case opts[:separate_streams] do + true -> raw + _ -> {:ok, stdout, stderr, status} = raw + {:ok, stdout <> stderr, status} + end + end + end diff --git a/mix.exs b/mix.exs index a3448e2..08da422 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule SSHEx.Mixfile do def project do [app: :sshex, - version: "1.0.0", + version: "1.1.0", elixir: "~> 1.0.0", package: package, description: "Simple SSH helpers for Elixir" ]