Home > Blockchain >  How Can I Get System.cmd to End Normally when expecting input on STDIN?
How Can I Get System.cmd to End Normally when expecting input on STDIN?

Time:01-07

I've spotted something that I find very puzzling about the behavior of System.cmd. I just wanted to ask if anyone might have thoughts on what I may be doing wrong or what may be going on.

I've been trying to wrap an Elixir script around the ack programmer's grep. I tried this:

{_message, errlevel} = System.cmd("ack",[])

And I get back the help text that ack displays on an empty command line; I won't bother to reproduce it here because it's not necessarily germane to the question.

Then I try this:

{_message, errlevel} = System.cmd("ack",[""])

And it looks like iex hangs. Now I realize in the first case the output may be going to stderr rather than stdout. But there's another reason I'm asking about this; I found something even more interesting to me. Because I'm not 100% committed to using ack I thought I'd try ripgrep on the thought that it might interact with stdout better.

So if I do this:

{_message, errlevel} = System.cmd("rg",[])

Same as ack with no arguments--shows me the help text. Again I'm guessing it's probably out to stderr. I could check to confirm my assumption but what's even more interesting to me is that when I do this:

{_message, errlevel} = System.cmd("rg",[""])

It hangs again!

I had always figured the issue is with how ack interacts with stdout but now I'm not so sure since I see the same behavior with ripgrep. This is Elixir 1.13.2 on MacOSX 13.1. I've seen this same behavior with older versions of MacOSX.

Any idea how I can get the ack and/or ripgrep process to terminate so I get a response back? I've seen this https://hexdocs.pm/elixir/main/Port.html#module-zombie-operating-system-processes and I can try it but I was hoping for something slightly less hacky, I guess. Any suggestions? Also if I use the :stderr_to_stdout option set to true, it doesn't seem to make any difference.

I've seen this Q & A but I'm not totally clear on how using Task.start_link would help in this case. I mean would one do a Task.start_link on System.cmd?

CodePudding user response:

Looks like a bug. I've filed https://github.com/elixir-lang/elixir/issues/12321.

CodePudding user response:

You are executing a command that expects input on STDIN, but with System.cmd/3, there is no mechanism to provide the input.

Elixir has no way to know the behaviour of the command you are executing, so waits for the process to terminate, which never happens. As José mentioned on the issue Roger Lipscombe raised, this is expected behaviour.

If you want to send the OS process input via STDIN, you need to use Ports. However, there are limitations there too, which I asked about here.

For ack specifically, it reads from STDIN if you don't provide a filename. So you can workaround the limitation by putting the data in a file, and providing the filename as an argument, rather than piping the data via OS streams.

  • Related