Ian Hunter
Ian Hunter

Reputation: 9774

Force carriage returns in IEx Ports' stderr?

If I open a Port in IEx for a script that prints anything to stderr, none of the output is printed with carriage returns. How can I fix this? I am running external software whose output I cannot control, so I can't just add the returns manually.

Example

In /tmp/run.sh

#!/usr/bin/env bash
>&2 echo -e "line 1\nline 2\nline 3\nline 4"

In an IEx shell

iex(1)> Port.open({:spawn_executable, "/tmp/run.sh"}, [])
line 1             
      line 2                                                                                                   
            line 3                                                                                             
                  line 4

Upvotes: 1

Views: 258

Answers (2)

z5ottu
z5ottu

Reputation: 107

I have another quick and dirty solution (Elixir only)

Port.open({:spawn_executable, "/tmp/run.sh"}, [:use_stdio, :stderr_to_stdout, :binary, :hide]); receive do
  {_port, {:data, line}} -> String.split(line,"\n", trim: true) |> Enum.each(fn(x)-> IO.puts(x) end)
end

The question is what do you want to do with the data.

More info on link https://bugs.erlang.org/browse/ERL-802 quote:

elixir --no-halt -e ':user_drv.start();Port.open({:spawn, "bash -c '\''>&2 echo \"stderr\nline2\"'\''"}, [:binary])' 2> err.log

Upvotes: 0

Steve Vinoski
Steve Vinoski

Reputation: 20024

You can run the script under a wrapper that inserts a carriage return before the newline. Here's one such wrapper that uses bash and perl:

#!/usr/bin/env bash
"$@" 2>&1 | perl -pe 's/\n/\r\n/' 1>&2

Here's another using bash and unix2dos:

#!/usr/bin/env bash
"$@" 2>&1 | unix2dos 1>&2

Basically, anything that can read the original program's stderr, replace newline with a carriage return and newline combination, and then write the result to stderr will work.

Put one of these solutions in a file named /tmp/lf.sh. Below we run it from iex with your original /tmp/run.sh, first with just the original script and then with the wrapper:

iex(1)> Port.open({:spawn_executable, "/tmp/run.sh"}, [])
#Port<0.5>
iex(2)> line 1
              line 2
                    line 3
                          line 4

nil
iex(3)> Port.open({:spawn_executable, "/tmp/lf.sh"}, [args: ["/tmp/run.sh"]])
#Port<0.6>
iex(4)> line 1
line 2
line 3
line 4

nil
iex(5)>

Upvotes: 2

Related Questions