我发现了System.cmd的一些令人困惑的行为。我只是想问问有没有人对我可能做错了什么或可能发生了什么有什么想法。
我一直在试图包装一个Elixir脚本围绕ack程序员的grep。我试过了:
{_message, errlevel} = System.cmd("ack",[])
我得到了ack在空命令行上显示的帮助文本;我在这里就不赘述了,因为它不一定与问题相关。
然后我试试这个:
{_message, errlevel} = System.cmd("ack",[""])
看起来iex挂起了。现在我意识到,在第一种情况下,输出可能是stderr而不是stdout。但我问这个问题还有另一个原因;我发现了更有趣的东西。因为我不是百分之百地承诺使用ack,我认为我应该尝试ripgrep,因为它可能与stdout更好地交互。
所以如果我这样做:
{_message, errlevel} = System.cmd("rg",[])
与不带参数的ack相同——显示帮助文本。我猜这可能是故意的。我可以检查一下以确认我的假设,但对我来说更有趣的是,当我这样做时:
{_message, errlevel} = System.cmd("rg",[""])
又挂起!
我一直认为问题是ack如何与stdout交互,但现在我不太确定,因为我看到了与ripgrep相同的行为。这是MacOSX 13.1上的Elixir 1.13.2。我在旧版本的MacOSX上看到过同样的行为。
是否知道如何让ack和/或ripgrep进程终止,以便我得到响应?我已经看到了这个https://hexdocs.pm/elixir/main/Port.html#module-zombie-operating-system-processes,我可以尝试一下,但我希望一些稍微不那么hacky,我猜。有什么建议吗?此外,如果我使用:stderr_to_stdout选项设置为true,它似乎没有任何区别。
我看过这个Q &但是我不完全清楚在这种情况下使用Task.start_link
会有什么帮助。我是说有人会做任务吗。System.cmd上的start_link ?
您正在执行一个期望在STDIN上输入的命令,但是对于System.cmd/3
,没有提供输入的机制。
Elixir无法知道你正在执行的命令的行为,所以等待进程终止,这永远不会发生。正如约瑟夫在罗杰·利普斯库姆提出的问题上所提到的,这是预期的行为。
如果你想通过STDIN发送操作系统进程输入,你需要使用Ports。然而,也有一些限制,我在这里问过。
对于ack
,如果您不提供文件名,它将从STDIN
读取。因此,您可以通过将数据放在文件中并提供文件名作为参数来解决此限制,而不是通过操作系统流传输数据。
看起来像一个bug。我已经提交了https://github.com/elixir-lang/elixir/issues/12321.