PHP 在 exec() bash 脚本时挂起


PHP hanging while exec() bash script

我有一些代码行看起来像这样...

exec($this->path.' start > /dev/null 2>&1 &');
return ['status' => 'Command executed'];
$this->path是一个 shell 脚本,

start 是 shell 脚本的参数,我相信该行的其余部分应该转储任何响应,以便 php 脚本可以继续运行。它没有像它应该的那样工作,php 成功启动了 shell 脚本(启动游戏服务器),但是 php 只是挂起,直到我使用 shell 关闭服务器。当我使用 shell 关闭服务器时,它完成执行,我收到"命令已执行"响应。我还禁用了 SELinux 的强制实施,以确保它不会干扰。

运行Linux - Fedora 21和内置的PHP开发服务器。

我相信该行的其余部分应该转储任何响应,以便 php 脚本可以继续运行

如果你不明白,这里是解释。如果您有:

exec($this->path.' start > /dev/null 2>&1 &');

> /dev/null部分意味着将标准输出(即命令产生的常规输出)重定向到/dev/null(即空设备)。因此,命令本身生成的任何输出都将被抑制。

2>&1部分表示将 stderror(即执行命令产生的任何错误)重定向到 stdout。但是,由于 stdout 被重定向到/dev/null,任何错误也会被重定向到那里。因此,对于这两个,它会抑制命令将生成的任何消息。

最后,末尾的&(& 符号)将命令分叉到新进程。从 Bash 手册页:

如果命令被控制运算符 & 终止,则外壳 在后台的子外壳中执行命令。外壳确实 不等待命令完成,返回状态为 0 (true)。

但是,根据这个问题,您正在做的事情应该是有效的。必须有其他事情发生以防止进程成功分叉。为了排除PHP成为问题,我将首先尝试通过命令行而不是通过PHP的exec执行命令。如果它仍然不起作用,我猜这是因为您的作业控制有问题。要么以某种方式禁用。我还没有在 PHP 中尝试过这个,但您可以使用 set -m 命令(启用作业控制)启用它。请注意,要禁用作业控制而不是set -m,请执行set +m .以下是在 PHP 中执行此操作的方法:

exec('set -m && ' . $this->path.' start > /dev/null 2>&1 &');

您可以做的另一件事是在执行 PHP 脚本时,登录到命令行并键入命令jobs并查看其输出。如果为空,则 PHP 没有正确分叉作业。您应该看到类似以下内容:

[1]+  Stopped                 your_command.sh

请注意,这里是如何说stopped的。如果进程仍在运行,则可能不应stopped

您可以做的另一件事是查看checkjobs是启用还是禁用。登录到服务器并执行以下操作以获取内置 shell 可选行为:

shopt -p | grep checkjobs

如果输出shopt -u checkjobs,这不是问题。如果它改为说shopt -s checkjobs,这可能会导致您看到的行为,因为使用后台作业杀死 shell 将导致错误,指出有作业正在运行,您实际上必须杀死 shell 两次才能摆脱它。也许这是PHP开发人员没有考虑的事情。在这种情况下,请在 PHP 中的命令之前加上shopt -u checkjobs &&

exec('shopt -u checkjobs && ' . $this->path.' start > /dev/null 2>&1 &');

我在生产环境中解决了同样的问题,如下所示:

pclose(popen($this->path.' start > /dev/null 2>&1 &', 'r'));

所以,诀窍是启动服务器,然后关闭进程文件指针

希望对:)有所帮助

一种不同的(丑陋的)方法是在屏幕会话中实际运行您的 shell 脚本。

exec('screen -dmS -X ' . $this->path . ' start');