通过 Erlang 端口调用时 Shell 脚本行为异常

发布于 2024-10-25 23:51:19 字数 2166 浏览 2 评论 0原文

当从 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或只是关闭窗口),这不适合我的要求。为什么行为上有差异?有办法解决吗?

When calling shell scripts from Erlang, I generally need their exit status (0 or something else), so I run them using this function:

%% 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.

This worked fine, until I used this to start a script which forks off a program and exits:

#!/bin/sh

FILENAME=$1

eog $FILENAME &

exit 0

(In the actual usecase, there are quite a few more arguments, and some massaging before they are passed to the program). When run from the terminal, it shows the image and exits immediately, as expected.

But running from Erlang, it doesn't. In the log file I see that it starts fine:

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

and the eog window appears. But I don't get

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

until killing the eog process (with kill or just closing the window), which isn't suitable for my requirements. Why the difference in behavior? Is there a way to fix it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

守不住的情 2024-11-01 23:51:20

通常,如果您在 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 视图中没有任何后台作业。

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

开始:

#!/bin/sh

FILENAME=$1

exec eog $FILENAME

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

Normally if you run a command in background with & in a shell script and the shell script terminates before the command, then the command gets orphaned. It might be that erlang trys to prevent orphaned processes in open_port and waits for eog to terminate. Normally if you want to run something in background during a shell script you should put in a wait at the end of the script to wait for your background processes to terminate. But this is exactly what youd don't want to do.

You might try the following in your shell script:

#!/bin/sh

FILENAME=$1

daemon eog $FILENAME

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

If your operating system has a daemon command. I checked in FreeBSD and it has one: daemon(8)

This is not a command available on all Unix alike systems, however there might be a different command doing the same thing in your operating system.

The daemon utility detaches itself from the controlling terminal and executes the program specified by its arguments.

I'm not sure if this solves your problem, but I suspect that eog somehow stays attached to stdin/stdou as a kind of controling terminal. Worth a try anyway.

This should also solve the possible problem that job control is on erroneously which could also cause the problem. Since daemon does exit normally your shell can't try to wait for the background job on exit because there is none in the shells view.

Having said all this: why not just keep the port open in Erlang while eog runs?

Start it with:

#!/bin/sh

FILENAME=$1

exec eog $FILENAME

Calling it with exec doesn't fork it bu replaces the shell process with eog. The exit status you'll see in Erlang will then be the status of eog when it terminates. Also you have the possibility to close the port and terminate eog from Erlang if you want to do so.

临走之时 2024-11-01 23:51:20

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

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

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

Perhaps your /bin/sh doesn't support job control when it isn't run interactively? At least the /bin/sh (actually dash(1)!) on my Ubuntu system mentions:

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

When you run the script from a terminal, the shell probably recognizes that it is being run interactively and supports job control. When you run the shell script as a port, the shell probably runs without job control.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文