Skip to content

Instantly share code, notes, and snippets.

@timruffles
Last active February 8, 2023 14:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timruffles/77e9b69cdecdd7b3ef08 to your computer and use it in GitHub Desktop.
Save timruffles/77e9b69cdecdd7b3ef08 to your computer and use it in GitHub Desktop.
erlang ports bewlider me
defmodule LearningPorts do
def main do
parent = self()
# if we need to control STDIN, the fun begins:
# unfortunately we can't close the port's stdin without closing the port.
# so it'll block waiting forever for an EOF that'll never appear.
#
# so we use a wrapper program that allows us to specify a sentinel line
# that stands in for EOF
spawn(fn() ->
port = Port.open {:spawn, "./stdout-wrap EOF tr f g"}, [{:cd,"#{__DIR__}/.."},:exit_status]
send port, {self(), {:command,"foooooo\nEOF"}}
receive_loop "wrapped"
send parent, :with_io
end)
wait_loop [:with_io]
end
def wait_loop [] do
:done
end
def wait_loop waiting do
receive do
x when is_atom(x) ->
wait_loop(waiting -- [x])
y ->
IO.inspect {"wait_loop suprised with:",y}
end
end
def receive_loop name do
IO.puts "Receive #{name}"
receive do
{_, {:data,x}} ->
IO.puts "#{name}: #{x}"
receive_loop name
{_,:eof} ->
IO.puts "#{name} eof"
:ok
{_,{:exit_status,status}} ->
IO.puts "#{name}: exit_status #{status}"
:ok
{:EXIT,_,_} ->
IO.puts "#{name}: exit"
:ok
x ->
IO.inspect {:unexpected,name,x}
receive_loop(name)
end
end
end
LearningPorts.main
> mix run lib/ports.exs
Receive wrapped
wrapped: goooooo
Receive wrapped
wrapped: exit_status 0
#!/usr/bin/env ruby
# wraps an external program for erlang's port system which
# cannot close stdin without closing the port
#
# stdout-wrap EOF-sentinel command arg0 arg1 .. argN
#
# EOF-sentinel is a string that when present on its own line
# will signal that stdin should be closed and passed to the program
sentinel = ARGV.shift
IO.popen(ARGV,"w+") do |io|
line = ""
while c = $stdin.getc do
if c == "\n"
io.puts line
line = ""
else
line += c
if line == sentinel
io.close_write
break
end
end
end
$stdout.write io.read
end
@ivan
Copy link

ivan commented Jul 1, 2018

Yeah, this is a pretty big Erlang annoyance. FWIW, you can also pipe through head -n LINES or head -c BYTES to close stdin.

@bezborodow
Copy link

See erlang/otp#4326 for further information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment