4

当从 Erlang 调用 shell 脚本时,我通常需要它们的退出状态(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

直到终止eog进程(kill关闭或关闭窗口),这不适合我的要求。为什么行为上的差异?有没有办法解决它?

4

2 回答 2

3

通常,如果您在后台使用&shell 脚本运行命令,并且 shell 脚本在命令之前终止,则该命令将被孤立。可能是 erlang 试图阻止 open_port 中的孤立进程并等待eog终止。通常,如果您想在 shell 脚本期间在后台运行某些东西,您应该在wait脚本末尾放置 a 以等待您的后台进程终止。但这正是你不想做的。

您可以在 shell 脚本中尝试以下操作:

#!/bin/sh

FILENAME=$1

daemon eog $FILENAME

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

如果您的操作系统有daemon命令。我检查了 FreeBSD,它有一个:daemon(8)

这不是在所有类似 Unix 的系统上都可用的命令,但是在您的操作系统中可能有不同的命令在做同样的事情。

守护程序实用程序将自己与控制终端分离并执行由其参数指定的程序。

我不确定这是否能解决您的问题,但我怀疑它eog以某种方式保持连接到 stdin/stdou 作为一种控制终端。总之值得一试。

这也应该解决作业控制错误打开的可能问题,也可能导致问题。由于daemon确实正常退出,因此您的 shell 无法尝试在退出时等待后台作业,因为 shell 视图中没有。

说了这么多:为什么不在运行时在 Erlang 中保持端口打开eog

从以下开始:

#!/bin/sh

FILENAME=$1

exec eog $FILENAME

调用它exec并不会派生它,而是将 shell 进程替换为eog. 您将在 Erlang 中看到的退出状态将是eog它终止时的状态。eog如果您愿意,您也可以关闭端口并从 Erlang 终止。

于 2011-03-22T15:19:30.107 回答
1

当它不以交互方式运行时,也许您/bin/sh不支持作业控制?至少我的 Ubuntu 系统上的/bin/sh(实际上dash(1)!)提到:

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

当您从终端运行脚本时,shell 可能会识别出它正在以交互方式运行并支持作业控制。当您将 shell 脚本作为端口运行时,shell 可能会在没有作业控制的情况下运行。

于 2011-03-22T12:01:01.803 回答