如何从 System.cmd 输出中捕获每一行?
How to capture each line from System.cmd output?
当 运行 命令来自 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
我还没有找到解决方案,有类似的问题但没有明确的答案:question or question
所以,这里有几种方法,我将尝试总结一下,让你能够理解之前在其他问题中的答案。
首先,你应该知道详细处理这个东西,你需要学习如何使用:erlang.open_port/2
。您可以将 {:line, max_length}
传递给选项以每行获取 1 条消息。您看到的 git
的输出是正在写入 stderr
的内容,您可以传递 :stderr_to_stdout
进行重定向,以便它们在每条消息中出现 1 行。您可以循环使用 receive
直到收到 eof
消息,您可以查看文档以了解有关何时发出 eof
消息的更多详细信息。
bitwalker 在你的第二个 link 中的回答将通过一些修改得到你想要的:
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 的垃圾了吗?进度就是这样出来的。您可以从 args 中删除 --progress
,您只会得到 1 或 2 行无聊的行。如果你想得到所有的东西,你需要流式传输结果,然后自己按行分割输出。
Shell.exec(gitcmd, ["clone","--progress",url], [:stream])
您可以看到,使用 :stream
而不是 :line
我们将获得从另一侧刷新的所有内容。您必须自己清理悬挂的 \r
和 \n
,并且您可能想要计划接收部分线路,但我认为这应该让您顺利开始旅程。
这一切都是关于将 stderr 重定向到 stdout,但是如果你需要实际保留 stderr
和 stdout
之间的区别,并且你想要终止进程的能力,而不是依赖在您关闭 stdin
后关闭它时,您将不得不依赖诸如 porcelain 之类的东西来管理一个行为良好的进程,该进程将为您 "manage" 另一个进程。
当 运行 命令来自 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
我还没有找到解决方案,有类似的问题但没有明确的答案:question or question
所以,这里有几种方法,我将尝试总结一下,让你能够理解之前在其他问题中的答案。
首先,你应该知道详细处理这个东西,你需要学习如何使用:erlang.open_port/2
。您可以将 {:line, max_length}
传递给选项以每行获取 1 条消息。您看到的 git
的输出是正在写入 stderr
的内容,您可以传递 :stderr_to_stdout
进行重定向,以便它们在每条消息中出现 1 行。您可以循环使用 receive
直到收到 eof
消息,您可以查看文档以了解有关何时发出 eof
消息的更多详细信息。
bitwalker 在你的第二个 link 中的回答将通过一些修改得到你想要的:
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 的垃圾了吗?进度就是这样出来的。您可以从 args 中删除 --progress
,您只会得到 1 或 2 行无聊的行。如果你想得到所有的东西,你需要流式传输结果,然后自己按行分割输出。
Shell.exec(gitcmd, ["clone","--progress",url], [:stream])
您可以看到,使用 :stream
而不是 :line
我们将获得从另一侧刷新的所有内容。您必须自己清理悬挂的 \r
和 \n
,并且您可能想要计划接收部分线路,但我认为这应该让您顺利开始旅程。
这一切都是关于将 stderr 重定向到 stdout,但是如果你需要实际保留 stderr
和 stdout
之间的区别,并且你想要终止进程的能力,而不是依赖在您关闭 stdin
后关闭它时,您将不得不依赖诸如 porcelain 之类的东西来管理一个行为良好的进程,该进程将为您 "manage" 另一个进程。