通过Erlang端口调用Shell脚本时表现奇怪

时间:2011-03-22 11:49:01

标签: bash erlang sh erlang-ports

从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或仅关闭窗口),这不符合我的要求。为什么行为不同?有办法解决吗?

2 个答案:

答案 0 :(得分:2)

通常,如果在shell脚本中使用&在后​​台运行命令,并且shell脚本在命令之前终止,则命令将变为孤立状态。可能是erlang试图阻止open_port中的孤立进程并等待eog终止。通常,如果要在shell脚本期间在后台运行某些内容,则应在脚本末尾放入wait以等待后台进程终止。但这正是你不想做的事情。

您可以在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视图中没有。

说完这一切:为什么不在eog运行时让Erlang保持打开状态?

以:

开头
#!/bin/sh

FILENAME=$1

exec eog $FILENAME

使用exec调用它不会将它替换为使用eog替换shell进程。您将在Erlang中看到的退出状态将在eog终止时成为状态。如果你想这样做,你也可以关闭端口并从Erlang终止eog

答案 1 :(得分:1)

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

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

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