我有一些看起来像这样的代码......
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开发服务器。
答案 0 :(得分:3)
我相信该行的其余部分是假设转储任何响应,以便php脚本可以继续运行
如果您不理解,请在此解释。如果你有:
exec($this->path.' start > /dev/null 2>&1 &');
> /dev/null
部分意味着将stdout(即命令生成的常规输出)重定向到/ dev / null(这是空设备)。因此,命令本身产生的任何输出都将被抑制。
2>&1
部分意味着将stderror(即执行命令产生的任何错误)重定向到stdout。但是,由于stdout被重定向到/ dev / null,因此任何错误也将被重定向到那里。因此,使用这两个,它会抑制命令将产生的任何消息。
最后,&
(&符号)最后将命令分配给新进程。来自Bash man page:
如果命令由控制操作员&,shell终止 在子shell中在后台执行命令。外壳确实如此 不等待命令完成,返回状态为0(true)。
但是,根据this question,你正在做什么应该正在运作。必须有其他事情来阻止该过程成功分叉。只是为了排除PHP的问题,我首先尝试通过命令行而不是PHP的exec执行命令。如果它仍然无法正常工作,我会猜测它是因为您的Job Control存在问题。它要么以某种方式被禁用。我还没有在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 optional behavior:
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 &');
答案 1 :(得分:0)
我在生产环境中解决了同样的问题,如下所示:
pclose(popen($this->path.' start > /dev/null 2>&1 &', 'r'));
因此,诀窍是启动服务器,然后关闭进程文件指针
希望有所帮助:)
答案 2 :(得分:0)
一种不同(丑陋)的方法是在屏幕会话中实际运行shell脚本。
exec('screen -dmS -X ' . $this->path . ' start');