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

时间:2010-12-07 13:26:34

标签: php javascript ajax bash

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

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

echo verifyResults($tmp);

(verifyResults函数将返回一些HTML,指示用户是否成功完成更新命令。即,如果更新成功完成,则显示重启设备的按钮等。)

这个问题是update命令需要几分钟才能完成,PHP脚本会阻塞,直到shell命令完成后再返回任何输出。这通常意味着更新命令将继续运行,而用户将看到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>';

这理论上会将命令放在后台并将所有输出附加到/tmp/output.txt。

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

但我在这里看到的问题是getOutput.php最终会在更新设备闪存的过程中无法访问,因为它位于更新目标分区上。所以这可能会让我处于和以前一样的位置,尽管没有504或看似永远加载的页面。

我可以将getOutput.php移动到设备中的另一个分区,但是我想我仍然需要使用webserver配置做一些时髦的东西才能在那里访问它(从webroot到它的符号链接,像任何其他文件一样,最终会在重新刷新期间被覆盖。)

在运行命令时是否有其他显示命令输出的方法,或者我应该使用我所拥有的解决方案?

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

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

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

的内容

但是,似乎output.txt不能实时反映re-flash命令的输出 - 在执行更新命令之前,它似乎什么也没有显示。我需要做进一步的测试,看看这里发生了什么。

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

要解决这个问题,我将把更新命令调用放在一个单独的脚本中并通过Ajax请求 - 这样,用户至少会收到一些预先打包的反馈,而技术上仍在等待系统。 / p>

4 个答案:

答案 0 :(得分:3)

答案 1 :(得分:1)

有趣的场景。

我的第一个想法是做一些关于proc_ *和$ _SESSION的事情,但我不确定这是否有效。试一试,但如果没有......

如果您担心在此过程中闪存文件,您可以始终在辅助进程中实例化mysql数据库并写入该数据库。数据库可以存在于另一个分区上,您可以通过本地IP进行寻址,系统将负责路由。

修改

当我提到proc_ *与会话时,我的意思是类似于this,其中$ descriptorspec将成为:

$_SESSION = array(
    1 => array("pipe", "w"),
);

然而,我有点怀疑它会起作用。该进程将最终写入内存中的$ _SESSION,一旦第一个脚本被杀死,该内容就不再存在。

修改2

实际上,您可以安装memcache并让您的辅助进程直接写入内存,然后由您的Web界面进程重新读取。

答案 2 :(得分:1)

如果您擦除DocRoot,则没有资源/脚本可以在此期间响应来自用户的请求。因此,您必须在执行擦除的同一请求中向用户发送更新。这需要您启动shell进程并立即返回PHP。这可以通过pcntl_fork()pcntl_exec()来完成。您的PHP脚本现在应该不断将shell脚本的输出发送到客户端。如果shell脚本附加到/ tmp中的文件,则可以fpassthru()该文件并清除它,直到shell脚本结束。

答案 3 :(得分:1)

关于

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

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

再次 - 我无法验证这一点,但如果您想测试它,请尝试重新打开并关闭每次写入的文件。这将证实或否认我的理论,您可以相应地修改您的方法。