Skip to content

Instantly share code, notes, and snippets.

@benhamill
Last active February 15, 2016 03:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benhamill/a3614d50b63b61484e75 to your computer and use it in GitHub Desktop.
Save benhamill/a3614d50b63b61484e75 to your computer and use it in GitHub Desktop.
Elixir Port Experiment

I'm trying to understand how Elixir's Port works. I'd like to interact with another command that's run and communicate over :stdio and detect when the other proccess ends in order to gracefully handle it. In my actual application, I'll send input to the other process, get the result and check it. If it's good, I'll repeat. However, if the other process exits after sending any result, my program should take some actions and then exit its self. This is meant to be normal behavior, not an error.

However, when I run the below toy script, I'm getting errors trying to deal with the other process exiting. The 3 other processes I wrote are meant to read one line, echo it back, then exit, while the toy script has 3 items to deal with (it should just do the first, then report exit and end).

Here's how it's running on my system:

● src ❱ ./input_test.exs --bash
Got back "Yeah\n".
Command exited.
** (EXIT from #PID<0.47.0>) :epipe
● src 1❱ ./input_test.exs --ruby
Got back "Yeah\n".
Command exited.
● src ❱ ./input_test.exs --elixir
Got back "Yeah\n".
Command exited.
** (EXIT from #PID<0.47.0>) :epipe
● src 1❱

Notice that with --ruby, the toy script handles the situations and exits normally, but with --bash and --elixir, it exits with code 1 (if the exit code of the previous command is not 0 it shows up right before the ❱ in my prmopt).

Some context about my system:

● src ❱ ruby --version
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin13]
● src ❱ elixir --version
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.2.2
● src ❱ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14)
Copyright (C) 2007 Free Software Foundation, Inc.

Possibly I'm not understanding how the :eof option to Port.open/2 works? Or that I should be using another option entirely? Thanks in advance for your help.

#!/usr/bin/env elixir
{options, _, _} = OptionParser.parse(
System.argv,
strict: [
ruby: :boolean,
elixir: :boolean,
bash: :boolean,
]
)
command = cond do
options[:elixir] -> ~S"""
elixir -e "
IO.read(:line)
|> String.strip
|> IO.puts
"
"""
options[:ruby] -> ~S"""
ruby -e "
STDOUT.sync = true
puts gets.strip
"
"""
true -> "head -n1"
end
port = Port.open({:spawn, command}, [:binary, :eof])
Enum.each [
~s(Yeah\n),
~s(Yep\n),
~s(Uh-huh\n),
], fn out->
Port.command(port, out)
receive do
{^port, {:data, result}} ->
IO.puts "Got back #{inspect result}."
{^port, :eof} ->
IO.puts "Command exited."
exit :normal
{^port, problem} ->
IO.puts "Port problem: #{inspect problem}."
exit :bad
problem ->
IO.puts "Problem: #{inspect problem}."
exit :bad
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment