Linux 在 PHP 中使用实时输出运行进程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1281140/
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
Run process with realtime output in PHP
提问by Maksim Vi.
I am trying to run a process on a web page that will return its output in realtime. For example if I run 'ping' process it should update my page every time it returns a new line (right now, when I use exec(command, output) I am forced to use -c option and wait until process finishes to see the output on my web page). Is it possible to do this in php?
我正在尝试在网页上运行一个进程,该进程将实时返回其输出。例如,如果我运行 'ping' 进程,它应该在每次返回新行时更新我的页面(现在,当我使用 exec(command, output) 时,我被迫使用 -c 选项并等到进程完成才能看到在我的网页上输出)。是否可以在 php 中执行此操作?
I am also wondering what is a correct way to kill this kind of process when someone is leaving the page. In case of 'ping' process I am still able to see the process running in the system monitor (what makes sense).
我也想知道当有人离开页面时杀死这种进程的正确方法是什么。在“ping”进程的情况下,我仍然能够看到系统监视器中运行的进程(这是有道理的)。
采纳答案by egafni
This worked for me:
这对我有用:
$cmd = "ping 127.0.0.1";
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
flush();
$process = proc_open($cmd, $descriptorspec, $pipes, realpath('./'), array());
echo "<pre>";
if (is_resource($process)) {
while ($s = fgets($pipes[1])) {
print $s;
flush();
}
}
echo "</pre>";
回答by Nathacof
If you're looking to run system commands via PHP look into, the exec documentation.
如果您希望通过 PHP 运行系统命令,请查看exec 文档。
I wouldn't recommend doing this on a high traffic site though, forking a process for each request is quite a hefty process. Some programs provide the option of writing their process id to a file such that you could check for, and terminate the process at will, but for commands like ping, I'm not sure that's possible, check the man pages.
不过,我不建议在高流量站点上执行此操作,为每个请求创建一个进程是一个相当繁重的过程。某些程序提供了将其进程 ID 写入文件的选项,以便您可以随意检查和终止进程,但对于 ping 之类的命令,我不确定这是否可行,请查看手册页。
You may be better served by simply opening a socket on the port you expect to be listening (IE: port 80 for HTTP) on the remote host, that way you know everything is going well in userland, as well as on the network.
只需在远程主机上您希望侦听的端口(即 HTTP 的端口 80)上打开一个套接字,您可能会得到更好的服务,这样您就知道用户空间和网络上的一切都进展顺利。
If you're attempting to output binary data look into php's header function, and ensure you set the proper content-type, and content-disposition. Review the documentation, for more information on using/disabling the output buffer.
如果您尝试输出二进制数据,请查看php 的标头函数,并确保设置正确的content-type和content-disposition。查看文档,了解有关使用/禁用输出缓冲区的更多信息。
回答by Todd Owen
First check whether flush()works for you. If it does, good, if it doesn't it probably means the web server is buffering for some reason, for example mod_gzip is enabled.
首先检查flush()是否适合你。如果是,很好,如果不是,则可能意味着 Web 服务器出于某种原因正在缓冲,例如启用了 mod_gzip。
For something like ping, the easiest technique is to loop within PHP, running "ping -c 1" multiple times, and calling flush() after each output. Assuming PHP is configured to abort when the HTTP connection is closed by the user (which is usually the default, or you can call ignore_user_abort(false) to make sure), then you don't need to worry about run-away ping processes either.
对于 ping 之类的东西,最简单的技术是在 PHP 中循环,多次运行“ping -c 1”,并在每次输出后调用 flush()。假设 PHP 配置为在用户关闭 HTTP 连接时中止(这通常是默认设置,或者您可以调用 ignore_user_abort(false) 来确定),那么您也不必担心失控的 ping 进程.
If it's really necessary that you only run the child process onceand display its output continuously, that may be more difficult -- you'd probably have to run it in the background, redirect output to a stream, and then have PHP echo that stream back to the user, interspersed with regular flush() calls.
如果确实有必要只运行一次子进程并连续显示其输出,那可能会更困难 - 您可能必须在后台运行它,将输出重定向到流,然后让 PHP 回显该流返回给用户,中间穿插着常规的 flush() 调用。
回答by Todd Owen
Try changing the php.ini file set "output_buffering = Off". You should be able to get the real time output on the page Use system command instead of exec.. system command will flush the output
尝试更改 php.ini 文件集“output_buffering = Off”。您应该能够在页面上获得实时输出 Use system command 而不是 exec.. system command 将刷新输出
回答by Robb
This is a nice way to show real time output of your shell commands:
这是显示 shell 命令实时输出的好方法:
<?php
header("Content-type: text/plain");
// tell php to automatically flush after every output
// including lines of output produced by shell commands
disable_ob();
$command = 'rsync -avz /your/directory1 /your/directory2';
system($command);
You will need this function to prevent output buffering:
您将需要此功能来防止输出缓冲:
function disable_ob() {
// Turn off output buffering
ini_set('output_buffering', 'off');
// Turn off PHP output compression
ini_set('zlib.output_compression', false);
// Implicitly flush the buffer(s)
ini_set('implicit_flush', true);
ob_implicit_flush(true);
// Clear, and turn off output buffering
while (ob_get_level() > 0) {
// Get the curent level
$level = ob_get_level();
// End the buffering
ob_end_clean();
// If the current level has not changed, abort
if (ob_get_level() == $level) break;
}
// Disable apache output buffering/compression
if (function_exists('apache_setenv')) {
apache_setenv('no-gzip', '1');
apache_setenv('dont-vary', '1');
}
}
It doesn't work on every server I have tried it on though, I wish I could offer advice on what to look for in your php configuration to determine whether or not you should pull your hair out trying to get this type of behavior to work on your server! Anyone else know?
虽然它不适用于我尝试过的每台服务器,但我希望我可以提供有关在您的 php 配置中查找内容的建议,以确定您是否应该尝试让这种类型的行为起作用在你的服务器上!还有人知道吗?
Here's a dummy example in plain PHP:
这是一个纯 PHP 的虚拟示例:
<?php
header("Content-type: text/plain");
disable_ob();
for($i=0;$i<10;$i++)
{
echo $i . "\n";
usleep(300000);
}
I hope this helps others who have googled their way here.
我希望这可以帮助那些在这里用谷歌搜索的人。
回答by raminious
try this (tested on Windows machine + wamp server)
试试这个(在 Windows 机器 + wamp 服务器上测试)
header('Content-Encoding: none;');
set_time_limit(0);
$handle = popen("<<< Your Shell Command >>>", "r");
if (ob_get_level() == 0)
ob_start();
while(!feof($handle)) {
$buffer = fgets($handle);
$buffer = trim(htmlspecialchars($buffer));
echo $buffer . "<br />";
echo str_pad('', 4096);
ob_flush();
flush();
sleep(1);
}
pclose($handle);
ob_end_flush();
回答by mpen
For command-line usage:
对于命令行使用:
function execute($cmd) {
$proc = proc_open($cmd, [['pipe','r'],['pipe','w'],['pipe','w']], $pipes);
while(($line = fgets($pipes[1])) !== false) {
fwrite(STDOUT,$line);
}
while(($line = fgets($pipes[2])) !== false) {
fwrite(STDERR,$line);
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
return proc_close($proc);
}
If you're trying to run a file, you may need to give it execute permissions first:
如果你想运行一个文件,你可能需要先给它执行权限:
chmod('/path/to/script',0755);
回答by romaninsh
A better solution to this old problem using modern HTML5 Server Side Events is described here:
这里描述了使用现代 HTML5 服务器端事件来解决这个老问题的更好方法:
http://www.w3schools.com/html/html5_serversentevents.asp
http://www.w3schools.com/html/html5_serversentevents.asp
Example:
例子:
http://sink.agiletoolkit.org/realtime/console
http://sink.agiletoolkit.org/realtime/console
Code: https://github.com/atk4/sink/blob/master/admin/page/realtime/console.php#L40
代码:https: //github.com/atk4/sink/blob/master/admin/page/realtime/console.php#L40
(Implemented as a module in Agile Toolkit framework)
(在 Agile Toolkit 框架中作为模块实现)
回答by Nux
I've tried various PHP execution commands on Windows and found that they differ quite a lot.
我在 Windows 上尝试了各种 PHP 执行命令,发现它们有很大不同。
- Don't work for streaming:
shell_exec
,exec
,passthru
- Kind of works:
proc_open
,popen
-- "kind of" because you cannot pass arguments to your command (i.e. wont' work withmy.exe --something
, will work with_my_something.bat
).
- 不适用于流媒体:
shell_exec
,exec
,passthru
- 工作种类:
proc_open
,popen
-- “种类” 因为您不能将参数传递给您的命令(即不会与 一起工作my.exe --something
,将与 一起工作_my_something.bat
)。
The best (easiest) approach is:
最好(最简单)的方法是:
- You must make sure your exe is flushing commands (see printf flushing problem). Without this you will most likely receive batches of about 4096 bytes of text whatever you do.
- If you can, use
header('Content-Type: text/event-stream');
(instead ofheader('Content-Type: text/plain; charset=...');
). This will not work in all browsers/clients though! Streaming will work without this, but at least first lines will be buffered by the browser. - You also might want to disable cache
header('Cache-Control: no-cache');
. - Turn off output buffering (either in php.ini or with
ini_set('output_buffering', 'off');
). This might also have to be done in Apache/Nginx/whatever server you use in front. - Turn of compression (either in php.ini or with
ini_set('zlib.output_compression', false);
). This might also have to be done in Apache/Nginx/whatever server you use in front.
- 您必须确保您的 exe 正在刷新命令(请参阅printf 刷新问题)。没有这个,无论你做什么,你很可能会收到大约 4096 字节的文本批次。
- 如果可以,请使用
header('Content-Type: text/event-stream');
(而不是header('Content-Type: text/plain; charset=...');
)。但这不适用于所有浏览器/客户端!流媒体将在没有这个的情况下工作,但至少第一行将由浏览器缓冲。 - 您可能还想禁用缓存
header('Cache-Control: no-cache');
。 - 关闭输出缓冲(在 php.ini 或 with 中
ini_set('output_buffering', 'off');
)。这可能也必须在 Apache/Nginx/你前面使用的任何服务器中完成。 - 开始压缩(在 php.ini 中或使用
ini_set('zlib.output_compression', false);
)。这可能也必须在 Apache/Nginx/你前面使用的任何服务器中完成。
So in your C++ program you do something like (again, for other solutions see printf flushing problem):
因此,在您的 C++ 程序中,您会执行以下操作(同样,对于其他解决方案,请参见printf 刷新问题):
Logger::log(...) {
printf (text);
fflush(stdout);
}
In PHP you do something like:
在 PHP 中,您可以执行以下操作:
function setupStreaming() {
// Turn off output buffering
ini_set('output_buffering', 'off');
// Turn off PHP output compression
ini_set('zlib.output_compression', false);
// Disable Apache output buffering/compression
if (function_exists('apache_setenv')) {
apache_setenv('no-gzip', '1');
apache_setenv('dont-vary', '1');
}
}
function runStreamingCommand($cmd){
echo "\nrunning $cmd\n";
system($cmd);
}
...
setupStreaming();
runStreamingCommand($cmd);
回答by Pixsa
Checked all answers, nothing works...
检查了所有答案,没有任何效果...
Found solution Here
在这里找到解决方案
It works on windows (i think this answer is helpful for users searching over there)
它适用于 Windows(我认为这个答案对在那里搜索的用户有帮助)
<?php
$a = popen('ping www.google.com', 'r');
while($b = fgets($a, 2048)) {
echo $b."<br>\n";
ob_flush();flush();
}
pclose($a);
?>