Last active
February 8, 2023 14:53
-
-
Save timruffles/77e9b69cdecdd7b3ef08 to your computer and use it in GitHub Desktop.
erlang ports bewlider me
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> mix run lib/ports.exs | |
Receive wrapped | |
wrapped: goooooo | |
Receive wrapped | |
wrapped: exit_status 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yeah, this is a pretty big Erlang annoyance. FWIW, you can also pipe through
head -n LINES
orhead -c BYTES
to close stdin.