Принудительно вернуть каретку в stderr портов IEx?

Если я открою порт в IEx для сценария, который печатает что-либо в stderr, ни один вывод не будет напечатан с возвратом каретки. Как я могу это исправить? Я запускаю внешнее программное обеспечение, вывод которого я не могу контролировать, поэтому я не могу просто добавить возвраты вручную.

Пример

In /tmp/run.sh

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

В оболочке IEx

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

person Ian Hunter    schedule 25.03.2020    source источник
comment
Нашел эту очень старую ветку без опубликованного решения: erlang.org/ pipermail/erlang-questions/2010-февраль/   -  person Dogbert    schedule 26.03.2020


Ответы (2)


Вы можете запустить скрипт под оболочкой, которая вставляет возврат каретки перед новой строкой. Вот одна из таких оболочек, использующая bash и perl:

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

Вот еще одно использование bash и unix2dos:

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

По сути, все, что может прочитать stderr исходной программы, заменить новую строку комбинацией возврата каретки и новой строки, а затем записать результат в stderr, будет работать.

Поместите одно из этих решений в файл с именем /tmp/lf.sh. Ниже мы запускаем его из iex с вашим оригинальным /tmp/run.sh, сначала только с оригинальным скриптом, а затем с оберткой:

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)>
person Steve Vinoski    schedule 26.03.2020

У меня есть еще одно быстрое и грязное решение (только Эликсир)

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

Вопрос в том, что вы хотите делать с данными.

Дополнительная информация по ссылке https://bugs.erlang.org/browse/ERL-802 цитата:

elixir --no-halt -e ':user_drv.start();Port.open({:spawn, "bash -c '\''>&2 echo \"stderr\nline2\"'\''"}, [:binary])' 2> err.log
person z5ottu    schedule 26.03.2020
comment
Спасибо — моя главная проблема в том, что я хотел бы сохранить stderr в stderr для целей ведения журнала. Есть ли способ захватить stderr, не присоединяя его к stdout? - person Ian Hunter; 26.03.2020