Сценарий оболочки ведет себя странно при вызове через порт Erlang - PullRequest
4 голосов
/ 22 марта 2011

При вызове сценариев оболочки из Erlang мне обычно требуется их состояние выхода (0 или что-то еще), поэтому я запускаю их с помощью этой функции:

%% in module util
os_cmd_exitstatus(Action, Cmd) ->
    ?debug("~ts starting... Shell command: ~ts", [Action, Cmd]),
    try erlang:open_port({spawn, Cmd}, [exit_status, stderr_to_stdout]) of
        Port -> 
            os_cmd_exitstatus_loop(Action, Port)
    catch
        _:Reason ->
            case Reason of
                badarg ->
                    Message = "Bad input arguments";
                system_limit ->
                    Message = "All available ports in the Erlang emulator are in use";
                _ ->
                    Message = file:format_error(Reason)
            end,
            ?error("~ts: shell command error: ~ts", [Action, Message]),
            error
    end.

os_cmd_exitstatus_loop(Action, Port) ->
    receive
        {Port, {data, Data}} ->
            ?debug("~ts... Shell output: ~ts", [Action, Data]),
            os_cmd_exitstatus_loop(Action, Port);
        {Port, {exit_status, 0}} ->
            ?info("~ts finished successfully", [Action]),
            ok;
        {Port, {exit_status, Status}} ->
            ?error("~ts failed with exit status ~p", [Action, Status]),
            error;
        {'EXIT', Port, Reason} ->
            ?error("~ts failed with port exit: reason ~ts", 
                         [Action, file:format_error(Reason)]),
            error
    end.

Это работало нормально, пока я не использовал это для запускаскрипт, который разветвляет программу и завершает работу:

#!/bin/sh

FILENAME=$1

eog $FILENAME &

exit 0

(В реальном случае использования есть еще несколько аргументов и некоторый массаж, прежде чем они будут переданы программе).При запуске из терминала он показывает изображение и сразу же завершает работу, как и ожидалось.

Но при запуске из Erlang это не так.В файле журнала я вижу, что он запускается нормально:

22/Mar/2011 13:38:30.518  Debug: Starting player starting... Shell command: /home/aromanov/workspace/gmcontroller/scripts.dummy/image/show-image.sh /home/aromanov/workspace/media/images/9e89471e-eb0b-43f8-8c12-97bbe598e7f7.png

и появляется окно eog.Но я не получаю

22/Mar/2011 13:47:14.709  Info: Starting player finished successfully

, пока не убью процесс eogkill или просто закрыв окно), что не подходит для моих требований.Почему разница в поведении?Есть ли способ это исправить?

Ответы [ 2 ]

2 голосов
/ 22 марта 2011

Обычно, если вы запускаете команду в фоновом режиме с & в сценарии оболочки, и сценарий оболочки завершается до выполнения команды, то команда теряется. Возможно, что erlang пытается предотвратить потерянные процессы в open_port и ожидает завершения eog. Обычно, если вы хотите запустить что-то в фоновом режиме во время сценария оболочки, вы должны поместить wait в конце сценария, чтобы дождаться завершения фоновых процессов. Но это именно то, что вы не хотите делать.

Вы можете попробовать следующее в вашем скрипте:

#!/bin/sh

FILENAME=$1

daemon eog $FILENAME

# exit 0 not needed: daemon returns 0 if everything is ok

Если в вашей операционной системе есть команда daemon. Я проверил во FreeBSD, и он имеет один: daemon (8)

Это не команда, доступная во всех аналогичных системах Unix, однако может быть другая команда, выполняющая то же самое в вашей операционной системе.

Утилита daemon отсоединяется от управляющего терминала и выполняет программу, указанную в ее аргументах.

Я не уверен, что это решит вашу проблему, но я подозреваю, что eog каким-то образом остается подключенным к stdin / stdou как своего рода управляющий терминал. В любом случае, стоит попробовать.

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

Сказав все это: почему бы просто не оставить порт открытым в Erlang, пока eog работает?

Начните с:

#!/bin/sh

FILENAME=$1

exec eog $FILENAME

Вызов его с помощью exec не является форком, но процесс оболочки заменяется на eog. Статус выхода, который вы увидите в Erlang, будет иметь статус eog, когда он завершится. Также у вас есть возможность закрыть порт и прекратить eog из Erlang, если вы хотите это сделать.

1 голос
/ 22 марта 2011

Возможно, ваш /bin/sh не поддерживает управление заданиями, когда он не работает в интерактивном режиме? По крайней мере /bin/sh (на самом деле dash(1)!) В моей системе Ubuntu упоминает:

      -m monitor       Turn on job control (set automatically
                       when interactive).

Когда вы запускаете скрипт из терминала, оболочка, вероятно, распознает, что он запускается в интерактивном режиме, и поддерживает управление заданиями. Когда вы запускаете сценарий оболочки в качестве порта, оболочка, вероятно, запускается без контроля задания.

...