There are two problems with this code:
The first issue, as the documentation for IO.binwrite
mentions:
Important: do not use this function on IO devices in Unicode mode as it will write the wrong data. In particular, the standard IO device is set to Unicode by default, so writing to stdio with this function will likely result in the wrong data being sent down the wire.
The actual data that the first half of that pipeline prints out is 384 bytes long, because each byte of the second half of the data is translated to the corresponding Unicode character (so 128
→ <<194, 128>>
Calling :io.setopts(:standard_io, [encoding: :latin1])
before binwrite
avoids this behavior and writes exactly the 256 bytes expected.
The second issue is that IO.binread
also behaves unexpectedly with a Unicode device. Reading a single byte from it will fail if bytes farther along in the stream are invalid Unicode. so:
echo $'\x01\x02' | elixir -e ':file.read(:standard_io, 1) |> IO.inspect; :file.read(:standard_io, 1) |> IO.inspect'
# prints
{:ok, <<1>>}
{:ok, <<2>>}
# but this doesn't work even though the first two bytes are both OK
echo $'\x01\x02\xFF' | elixir -e ':file.read(:standard_io, 1) |> IO.inspect; :file.read(:standard_io, 1) |> IO.inspect'
# prints
{:error, :collect_chars}
:eof
This {:error, :collect_chars}
is swallowed by higher levels of the IO machinery and produces the “unknown POSIX error” explanation you’re seeing.
As in the first case, setting the encoding correctly produces the correct result:
echo $'\x01\x02\xFF' | elixir -e ':io.setopts(:standard_io, [encoding: :latin1]); :file.read(:standard_io, 1) |> IO.inspect; :file.read(:standard_io, 1) |> IO.inspect'
# prints
{:ok, <<1>>}
{:ok, <<2>>}
(note: this is the same collect_chars
error shape as in this ticket that @josevalim just filed, but I don’t believe it’s directly related)
Putting both together does what you want:
elixir -e ':io.setopts(:standard_io, [encoding: :latin1]); :binary.list_to_bin( for n <- 0..255, into: [], do: n) |> IO.binwrite()' | elixir -e ':io.setopts(:standard_io, [encoding: :latin1]); IO.binstream(:stdio, 1) |> Enum.each(&IO.inspect/1)'
# prints
<<0>>
...
<<255>>