如何进行非阻塞 php exec 调用?



我需要将文本回显到Linux中的命名管道(FIFO)。即使我在后台使用"&"运行并将所有输出重定向到/dev/null,shell_exec调用也始终阻塞。

互联网上有很多关于这个问题的答案,它们基本上都指向以下 php 手册部分:

如果程序使用此函数启动,为了使它继续在后台运行,必须将程序的输出重定向到文件或其他输出流。否则将导致 PHP 挂起,直到程序执行结束。

果然,当我尝试使用其他命令(如睡眠)的非阻塞方法(后台和重定向到/dev/null)时,php 成功执行而不会挂起。但是对于回显到 FIFO 的情况,即使使用 bash 运行相同的命令不会产生可见的输出并立即返回到 shell,php 也会挂起。

在 bash 中,我可以运行:

bash$ { echo yay > fifo & } &> /dev/null
bash$ cat fifo
yay
[1]+  Done                    echo yay > fifo

但是当使用php echo.php运行以下 PHP 文件时:

<?php
shell_exec("{ echo yay > fifo & } &> /dev/null");
?>

它挂起,除非我先打开 FIFO 进行阅读。

所以我的问题是,为什么这是阻塞,而睡眠不是?此外,我想知道幕后发生了什么:当我在 php 调用中输入"&"时,即使shell_exec调用阻止,echo调用显然不会阻止 php 调用它的任何 bash 会话,因为当我CTRL+Cphp 时,我可以从 FIFO 中读取"yay"(如果我不后台使用echo命令,CTRL+C后,FIFO 不包含任何文本)。这表明 php 在进入下一条指令之前可能正在等待echo命令的 pid。这是真的吗?

我一直在尝试类似的东西,最后想出了这个解决方案:

/**
* This runs a shell command on the server under the current PHP user, that is in CLI mode it is the user you are logged in with.
* If a command is run in the background the method will return the location of the tempfile that captures the output. In that case you will have to manually remove the temporary file.
*/
static public function command($cmd, $show_output = true, $escape_command = false, $run_in_background = false)   
{ 
if ($escape_command)
$cmd = escapeshellcmd($cmd);
$f = trim(`mktemp`);
passthru($cmd . ($show_output ? " | tee $f" : " > $f") . ($run_in_background ? ' &' : '')); 
return $run_in_background ? $f : trim(`cat $f ; rm -rf $f`);
}

诀窍是将输出写入临时文件,并在命令完成时返回该文件(阻塞行为)或仅返回文件路径(非阻塞行为)。此外,我使用的是passthru而不是shell_exec因为由于阻止行为,后者无法进行交互式会话。

最新更新