我有一些代码行看起来像这样...
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');