Php 在命令运行时从shell_exec命令获取输出

Php 在命令运行时从shell_exec命令获取输出,php,javascript,ajax,bash,Php,Javascript,Ajax,Bash,我正在编写一个PHP脚本化的网页,用于接受先前上传到服务器的JFFS2图像的文件名。然后,脚本将使用映像在服务器上重新刷新分区,并输出结果。我一直在使用这个: $tmp = shell_exec("update_flash -v " . $filename . " 4 2>&1"); echo '<h3>' . $tmp . '</h3>'; echo verifyResults($tmp); $tmp=shell_exec(“更新闪存-v.$filen

我正在编写一个PHP脚本化的网页,用于接受先前上传到服务器的JFFS2图像的文件名。然后,脚本将使用映像在服务器上重新刷新分区,并输出结果。我一直在使用这个:

$tmp = shell_exec("update_flash -v " . $filename . " 4 2>&1");
echo '<h3>' . $tmp . '</h3>';

echo verifyResults($tmp);
$tmp=shell_exec(“更新闪存-v.$filename.”;
回显“”$tmp",;
回声验证结果($tmp);
(verifyResults函数将返回一些HTML,指示用户更新命令是否成功完成。即,如果更新成功完成,则显示一个按钮以重新启动设备,等等。)

问题是update命令需要几分钟才能完成,PHP脚本会一直阻塞,直到shell命令完成后,才会返回任何输出。这通常意味着update命令将继续运行,而用户将看到HTTP 504错误(最坏情况下)或等待页面加载几分钟

我在考虑做类似的事情:

shell_exec("rm /tmp/output.txt");
shell_exec("update_flash -v " . $filename . " 4 2>&1 >> /tmp/output.txt &");
echo '<div id="output"></div>';
echo '<div id="results"></div>';
shell_exec(“rm/tmp/output.txt”);
shell_exec(“update_flash-v.$filename.”4 2>&1>>/tmp/output.txt&);
回声';
回声';
理论上,这将把命令放在后台,并将所有输出附加到/tmp/output.txt

然后,在一个Javascript函数中,我会定期请求getOutput.php,它只需打印/tmp/output.txt的内容并将其粘贴到“output”div中。命令完成后,另一个Javascript函数将处理输出并在“results”div中显示结果

但我在这里看到的问题是,getOutput.php在更新设备闪存的过程中最终将变得不可访问,因为它位于更新的目标分区上。因此,这可能会让我处于与以前相同的位置,尽管没有504或似乎永远加载的页面

我可以将getOutput.php移动到设备中的另一个分区,但我认为我仍然需要对Web服务器配置做一些奇怪的事情,以便能够在那里访问它(与其他任何文件一样,从webroot指向它的符号链接最终会在重新刷新时被覆盖)

有没有其他方法可以在命令运行时显示它的输出,或者我应该使用现有的解决方案

编辑1:我目前正在测试一些解决方案。稍后我将用结果更新我的问题

编辑2:文件系统似乎没有像我最初认为的那样被覆盖。相反,系统似乎以只读模式挂载现有文件系统,因此即使文件系统重新刷新,我仍然可以访问getOutput.php

我在问题中描述的第二个解决方案似乎与使用popen(如下面的回答所述)而不是shell_exec一起工作。页面加载后,我可以通过Ajax显示output.txt的内容

然而,output.txt似乎不能实时反映re flash命令的输出——在update命令执行返回之前,它似乎什么也不显示。我需要做进一步的测试,看看这里发生了什么

编辑3:没关系,当我访问文件时,它看起来像是当前文件。当内核执行一些与JFFS2相关的任务时,我遇到了一个延迟,这些任务是由我使用存储源JFFS2映像的分区触发的。我不知道为什么,但这显然会导致所有PHP脚本在完成之前都被阻塞

为了解决这个问题,我将把update命令调用放在一个单独的脚本中,并通过Ajax请求它——这样,用户在技术上仍然等待系统时,至少会收到一些预先打包的反馈。

看看popen:

有趣的场景

我的第一个想法是做一些关于proc_*和$_会话的事情,但我不确定这是否有效。试试看,如果不行

如果您担心文件在这个过程中被刷新,那么您可以在第二个过程中实例化一个mysql数据库并写入该数据库。数据库可以存在于另一个分区上,您可以通过本地ip对其进行寻址,系统将负责路由

编辑

当我提到proc_*with sessions时,我指的是类似于$descriptorspec的地方:

$_SESSION = array(
    1 => array("pipe", "w"),
);
然而,我有点怀疑这会起作用。进程将结束写入内存中的$\u会话,一旦第一个脚本被终止,该会话将不再存在

编辑2


实际上,在这一点上,您可以安装辅助进程并将其直接写入内存,然后通过web接口的进程重新读取内存。

如果您擦除DocRoot,则在此期间没有任何资源/脚本可以响应用户的请求。因此,您必须以执行擦除的相同请求向用户发送更新。这需要启动shell进程并立即返回PHP。这可以通过
pcntl\u fork()
pcntl\u exec()
实现。您的PHP脚本现在应该连续地将shell脚本的输出发送到客户端。如果shell脚本附加到/tmp中的一个文件,您可以
fpassthru()
该文件并将其清除,直到shell脚本结束。

关于您的但是

我猜您正在尝试将该文件用作流。我还没有做任何生产测试,但我相信该文件只会在fclose()上写回磁盘

如果在脚本#2中不断写入文件,那么这些写入实际上会直接进入内存,直到文件关闭

再次-我无法验证这一点,但如果您想测试它,请尝试在每次写入时重新打开和关闭文件。这将证实或否定我的理论,你可以