如何从System.cmd输出中捕获每一行?

时间:2016-02-08 14:24:09

标签: elixir phoenix-framework

如何从System.cmd或其他方法运行命令时捕获在控制台中写入的每一行?

我甚至想要捕获最终结果,但在这种情况下控制台中显示的内容类似于:Cloning into 'myrepo'... remote: Counting objects: 3271, done.并通过频道发送每一行:

case System.cmd("git", ["clone", "git@github.com:#{vault}/#{repo}.git"], cd: repo) do
  {results, 0} ->
    Myapp.Endpoint.broadcast("app:setup", "new:line", results)
  {_, code} ->
    raise RuntimeError, "`git clone` failed with code #{code}"
end

我没有找到解决方案,但有类似问题,但没有明确答案:questionquestion

1 个答案:

答案 0 :(得分:7)

所以,这里有几种方法,我将尝试总结并让你能够理解其他问题中的先前答案。

首先,您应该知道详细处理这些内容,您需要学习如何使用:erlang.open_port/2。您可以将{:line, max_length}传递给选项以获得每行1条消息。您看到的git输出是写入stderr的内容,您可以将:stderr_to_stdout传递给重定向,以便每条消息输入1行。您可以使用receive循环,直到收到eof消息,您可以查看文档以获取有关何时发出eof消息的更多详细信息。

bitwalker在你的第二个链接中的答案将通过一些修改得到你想要的东西:

defmodule Shell do
  def exec(exe, args, opts \\ [:stream]) when is_list(args) do
    port = Port.open({:spawn_executable, exe}, opts ++ [{:args, args}, :binary, :exit_status, :hide, :use_stdio, :stderr_to_stdout])
    handle_output(port)
  end

  def handle_output(port) do
    receive do
      {^port, {:data, data}} ->
        IO.inspect(data) # Replace this with the appropriate broadcast
        handle_output(port)
      {^port, {:exit_status, status}} ->
        status
    end
  end
end

现在,虽然我们可以将stderr重定向到stdout,但其中一个问题是git将检测重定向流并调整相应流的流量。我希望你比较这些调用的输出:

gitcmd = System.find_executable("git")
Shell.exec(gitcmd, ["clone","--progress",url], [{:line, 4096}])

这会在流中找到的每个“\ n”打印出1条消息。注意所有与\ r \ n的垃圾?这就是进步的结果。你可以从args中删除--progress,你只需要获得1或2条无聊线。如果你想得到所有东西,你需要流式传输结果,然后自己按行输出。

Shell.exec(gitcmd, ["clone","--progress",url], [:stream])

您可以看到使用:stream代替:line,我们将获得所有内容,因为它是从另一方刷新的。您必须自己清理悬空\r\n,并且您可能想要计划接收部分线路,但我认为这可以帮助您顺利完成旅程。

这完全是关于将stderr重定向到stdout,但是如果你需要实际保留stderrstdout之间的差异,并且你想要能够杀死进程,而不是依赖它关闭stdin后关闭,您将不得不依赖porcelain之类的东西来管理一个表现良好的流程,为您“管理”其他流程。