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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4377069/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 23:03:13  来源:igfitidea点击:

get output from shell_exec command as command runs

phpjavascriptajaxbash

提问by Jason Plank

I am coding a PHP-scripted web page that is intended to accept the filename of a JFFS2 image which was previously uploaded to the server. The script is to then re-flash a partition on the server with the image, and output the results. I had been using this:

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

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

echo verifyResults($tmp);

(The verifyResults function will return some HTML that indicates to the user whether the update command completed successfully. I.e., in the case that the update completes successfully, display a button to restart the device, etc.)

(verifyResults 函数将返回一些 HTML,向用户指示更新命令是否成功完成。即,在更新成功完成的情况下,显示一个按钮以重新启动设备等。)

The problem with this is that the update command takes several minutes to complete, and the PHP script blocks until the shell command is complete before it returns any of the output. This typically means that the update command will continue running, while the user will see an HTTP 504 error (at worst) or wait for the page to load for several minutes.

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

I was thinking about doing something like this instead:

我正在考虑做这样的事情:

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>';

This would theoretically put the command in the background and append all output to /tmp/output.txt.

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

And then, in a Javascript function, I would periodically request getOutput.php, which would simply print the contents of /tmp/output.txt and stick it into the "output" div. Once the command is completely done, another Javascript function would process the output and display a result in the "results" div.

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

But the problem I see here is that getOutput.php will eventually become inaccessible during the process of updating the device's flash memory, because it's on the partition to which is targeted for an update. So that could leave me in the same position as before, albeit without the 504 or a seemingly eternally-loading page.

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

I could move getOutput.php to another partition in the device, but then I think I would still have to do some funky stuff with the webserver configuration to be able to access it there (a symlink to it from the webroot would, like any other file, eventually be overwritten during the re-flash).

我可以将 getOutput.php 移动到设备中的另一个分区,但是我想我仍然需要对网络服务器配置做一些时髦的事情才能在那里访问它(从 webroot 到它的符号链接会像任何其他文件,最终在重新闪存期间被覆盖)。

Is there any other way of displaying the output of the command as it runs, or should I just make do with the solution I have?

有没有其他方法可以在命令运行时显示命令的输出,还是我应该使用我拥有的解决方案?

Edit 1:I'm currently testing some solutions. I'll update my question with results later.

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

Edit 2:It seems that the filesystem does not get overwritten as I had originally thought. Instead, the system seems to mount the existing filesystem in read-only mode, so I can still access getOutput.php even after the filesystem is re-flashed.

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

The second solution I described in my question does seem to work in addition with using popen (as mentioned in an answer below) instead of shell_exec. The page loads, and via Ajax I can display the contents of output.txt.

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

However, it seems that output.txt does not reflect the output from the re-flash command in real time-- it seems to display nothing until the update command returns from execution. I will need to do further testing to see what's going on here.

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

Edit 3:Never mind, it looks like the file iscurrent as I access it. I was just hitting a delay while the kernel did some JFFS2-related tasks triggered by my use of the partition on which the source JFFS2 image is stored. I don't know why, but this apparently causes all PHP scripts to block until it's done.

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

To work around that, I'm going to put the update command invocation in a separate script and request it via Ajax-- that way, the user will at least receive some prepackaged feedback while technically still waiting on the system.

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

采纳答案by Nicola Leoni

回答by Craige

Interesting scenario.

有趣的场景。

My first thought was to do something regarding proc_* and $_SESSION, but I'm not sure if that will work or not. Give it a try, but if not...

我的第一个想法是对 proc_* 和 $_SESSION 做一些事情,但我不确定这是否可行。尝试一下,但如果没有...

If you're worried about the file being flashed during the process, you could always instantiate a mysql database in the secondary process and write to that. The database can exist on another partition, and you can address it by local ip and the system will take care of the routing.

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

Edit

编辑

When I mentioned proc_* with sessions, I meant something similar to thiswhere $descriptorspec would become:

当我提到与会议proc_ *,我的意思是类似的东西其中$ descriptorspec将成为:

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

However I kind of doubt that will work. The process will end up writing to the $_SESSION in memory which no longer exists once the first script is killed.

但是我有点怀疑这会起作用。该进程最终将写入内存中的 $_SESSION,一旦第一个脚本被终止,该 $_SESSION 将不再存在。

Edit 2

编辑 2

ACTUALLY, on that note, you could install memcacheand have your secondary process write directly to memory, which can then be re-read by your web-interfaced process.

实际上,关于这一点,您可以安装memcache并将您的辅助进程直接写入内存,然后可以由您的 Web 接口进程重新读取。

回答by rik

If you wipe the DocRoot there is no resource/script that can respond to requests from the user during this time. Therefore you have to send updates to the user in the same request that does the wipe. This requires you to start the shell process and immediately return to PHP. This can be accomplished with pcntl_fork()and pcntl_exec(). Your PHP script should now continuously send the output of the shell script to the client. If the shell script appends to a file in /tmp, you could fpassthru()that file and clear it until the shell script ends.

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

回答by Craige

Regarding your However:

关于您的然而

My guess is you are trying to use the file as a stream. I haven't done any production tests, but I believe that the file will only be written back to disk on fclose().

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

If you are writing to the file continually in script #2, those writes are actually going directly into memory until the file is closed.

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

Again - I cannot verify this, but if you want to test it, try re-opening and closing the file for every write. This will confirm or deny my theory and you can modify your approach accordingly.

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