PHP中的命令行进度条

PHP中的命令行进度条,php,Php,我目前正在尝试向命令行脚本添加进度条,并尝试了各种解决方案(包括Zend和Console_ProgressBar)。它们共同的问题是,进度条不会固定在窗口的底部,因为在脚本期间,会输出新行和其他信息 有没有办法让进度条保持在终端的底部,但在脚本运行时仍能输出其他信息 [编辑] 我想出来了: 我不是直接输出到STDOUT,而是实际捕获变量内部的输出,而是使用echochr(27)擦除屏幕[2J'然后将变量的内容输出到标准输出,然后附加我的进度条 <? /* Copyright (c) 20

我目前正在尝试向命令行脚本添加进度条,并尝试了各种解决方案(包括Zend和Console_ProgressBar)。它们共同的问题是,进度条不会固定在窗口的底部,因为在脚本期间,会输出新行和其他信息

有没有办法让进度条保持在终端的底部,但在脚本运行时仍能输出其他信息

[编辑]

我想出来了:

我不是直接输出到STDOUT,而是实际捕获变量内部的输出,而是使用
echochr(27)擦除屏幕[2J'
然后将变量的内容输出到标准输出,然后附加我的进度条

<?
/*

Copyright (c) 2010, dealnews.com, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
 * Neither the name of dealnews.com, Inc. nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

 */

/**
 * show a status bar in the console
 *
 * <code>
 * for($x=1;$x<=100;$x++){
 *
 *     show_status($x, 100);
 *
 *     usleep(100000);
 *
 * }
 * </code>
 *
 * @param   int     $done   how many items are completed
 * @param   int     $total  how many items are to be done total
 * @param   int     $size   optional size of the status bar
 * @return  void
 *
 */

function show_status($done, $total, $size=30, $lineWidth=-1) {
    if($lineWidth <= 0){
        $lineWidth = $_ENV['COLUMNS'];
    }

    static $start_time;

    // to take account for [ and ]
    $size -= 3;
    // if we go over our bound, just ignore it
    if($done > $total) return;

    if(empty($start_time)) $start_time=time();
    $now = time();

    $perc=(double)($done/$total);

    $bar=floor($perc*$size);

    // jump to the begining
    echo "\r";
    // jump a line up
    echo "\x1b[A";

    $status_bar="[";
    $status_bar.=str_repeat("=", $bar);
    if($bar<$size){
        $status_bar.=">";
        $status_bar.=str_repeat(" ", $size-$bar);
    } else {
        $status_bar.="=";
    }

    $disp=number_format($perc*100, 0);

    $status_bar.="]";
    $details = "$disp%  $done/$total";

    $rate = ($now-$start_time)/$done;
    $left = $total - $done;
    $eta = round($rate * $left, 2);

    $elapsed = $now - $start_time;


    $details .= " " . formatTime($eta)." ". formatTime($elapsed);

    $lineWidth--;
    if(strlen($details) >= $lineWidth){
        $details = substr($details, 0, $lineWidth-1);
    }
    echo "$details\n$status_bar";

    flush();

    // when done, send a newline
    if($done == $total) {
        echo "\n";
    }

}

希望有意义:)

这是一个很好的cli进度条:


这是对上一个答案的改进,该答案处理终端大小并使用2行而不是1行。第一行是信息,如时间/百分比。第二行是进度条

<?
/*

Copyright (c) 2010, dealnews.com, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
 * Neither the name of dealnews.com, Inc. nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

 */

/**
 * show a status bar in the console
 *
 * <code>
 * for($x=1;$x<=100;$x++){
 *
 *     show_status($x, 100);
 *
 *     usleep(100000);
 *
 * }
 * </code>
 *
 * @param   int     $done   how many items are completed
 * @param   int     $total  how many items are to be done total
 * @param   int     $size   optional size of the status bar
 * @return  void
 *
 */

function show_status($done, $total, $size=30, $lineWidth=-1) {
    if($lineWidth <= 0){
        $lineWidth = $_ENV['COLUMNS'];
    }

    static $start_time;

    // to take account for [ and ]
    $size -= 3;
    // if we go over our bound, just ignore it
    if($done > $total) return;

    if(empty($start_time)) $start_time=time();
    $now = time();

    $perc=(double)($done/$total);

    $bar=floor($perc*$size);

    // jump to the begining
    echo "\r";
    // jump a line up
    echo "\x1b[A";

    $status_bar="[";
    $status_bar.=str_repeat("=", $bar);
    if($bar<$size){
        $status_bar.=">";
        $status_bar.=str_repeat(" ", $size-$bar);
    } else {
        $status_bar.="=";
    }

    $disp=number_format($perc*100, 0);

    $status_bar.="]";
    $details = "$disp%  $done/$total";

    $rate = ($now-$start_time)/$done;
    $left = $total - $done;
    $eta = round($rate * $left, 2);

    $elapsed = $now - $start_time;


    $details .= " " . formatTime($eta)." ". formatTime($elapsed);

    $lineWidth--;
    if(strlen($details) >= $lineWidth){
        $details = substr($details, 0, $lineWidth-1);
    }
    echo "$details\n$status_bar";

    flush();

    // when done, send a newline
    if($done == $total) {
        echo "\n";
    }

}

其他答案似乎过于复杂。我的解决方案是在下一次更新之前简单地回显\033[0G转义序列,并将光标移回开头

function progressBar($done, $total) {
    $perc = floor(($done / $total) * 100);
    $left = 100 - $perc;
    $write = sprintf("\033[0G\033[2K[%'={$perc}s>%-{$left}s] - $perc%% - $done/$total", "", "");
    fwrite(STDERR, $write);
}
第一次调用该函数会输出进度条,后续每次调用都会用新的进度条覆盖最后一行

<?
/*

Copyright (c) 2010, dealnews.com, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
 * Neither the name of dealnews.com, Inc. nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

 */

/**
 * show a status bar in the console
 *
 * <code>
 * for($x=1;$x<=100;$x++){
 *
 *     show_status($x, 100);
 *
 *     usleep(100000);
 *
 * }
 * </code>
 *
 * @param   int     $done   how many items are completed
 * @param   int     $total  how many items are to be done total
 * @param   int     $size   optional size of the status bar
 * @return  void
 *
 */

function show_status($done, $total, $size=30, $lineWidth=-1) {
    if($lineWidth <= 0){
        $lineWidth = $_ENV['COLUMNS'];
    }

    static $start_time;

    // to take account for [ and ]
    $size -= 3;
    // if we go over our bound, just ignore it
    if($done > $total) return;

    if(empty($start_time)) $start_time=time();
    $now = time();

    $perc=(double)($done/$total);

    $bar=floor($perc*$size);

    // jump to the begining
    echo "\r";
    // jump a line up
    echo "\x1b[A";

    $status_bar="[";
    $status_bar.=str_repeat("=", $bar);
    if($bar<$size){
        $status_bar.=">";
        $status_bar.=str_repeat(" ", $size-$bar);
    } else {
        $status_bar.="=";
    }

    $disp=number_format($perc*100, 0);

    $status_bar.="]";
    $details = "$disp%  $done/$total";

    $rate = ($now-$start_time)/$done;
    $left = $total - $done;
    $eta = round($rate * $left, 2);

    $elapsed = $now - $start_time;


    $details .= " " . formatTime($eta)." ". formatTime($elapsed);

    $lineWidth--;
    if(strlen($details) >= $lineWidth){
        $details = substr($details, 0, $lineWidth-1);
    }
    echo "$details\n$status_bar";

    flush();

    // when done, send a newline
    if($done == $total) {
        echo "\n";
    }

}
编辑: 我已将回音\r更改为转义序列\033[0G,现在应该可以在OSX和Linux/Unix上使用

编辑2: 根据@tbjers建议修复了第3行上可能出现的错误

编辑3: 更新了打印到STDERR的新版本,现在也在GitHub上:

编辑4: 现在使用composer:
composer需要macroman/终端进度条

use TerminalProgress\Bar;

$pg = new Bar(1000);

for ($i = 0; $i < 1000; $i++) {
    usleep(10000);
    $pg->tick();
}
使用TerminalProgress\Bar;
$pg=新棒材(1000);
对于($i=0;$i<1000;$i++){
美国LEEP(10000);
$pg->tick();
}

以下内容适用于unix机器

#!/usr/bin/php
<?php
$iloop = "0"; /* Outside the loop */
while (true){  
  $warn = "Program running hold on!!\r";
  if (strlen($warn) === $iloop+1){
    $iloop = "0";
  }
  $warn = str_split($warn);
  $iloop++;
  $warn[$iloop] = "\033[35;2m\e[0m".strtoupper($warn[$iloop]);
  echo " \033[7m".implode($warn);
  usleep(90000);
}
目标是检索当前终端总计列。(使用
tput

这是一个基地,准备扩大

 #!/usr/bin/php
 <?php

 @ob_start();
 
 $shell = system("tput cols");
 
 @ob_end_clean();
  
 for( $i= 0 ; $i < $shell ; $i++ ){ echo "█"; usleep(100000); } 
这显示了php
echo
的主要独特之处:它不追加新行,而bash
echo
追加新行

使用循环时,比如每隔一段时间检查一个条件, 另一个很好的警告跑步活动的方法是文本效果

#!/usr/bin/php
<?php
$iloop = "0"; /* Outside the loop */
while (true){  
  $warn = "Program running hold on!!\r";
  if (strlen($warn) === $iloop+1){
    $iloop = "0";
  }
  $warn = str_split($warn);
  $iloop++;
  $warn[$iloop] = "\033[35;2m\e[0m".strtoupper($warn[$iloop]);
  echo " \033[7m".implode($warn);
  usleep(90000);
}
下面使用
strtoupper
和ansi代码反向视频进行演示

#!/usr/bin/php
<?php
$iloop = "0"; /* Outside the loop */
while (true){  
  $warn = "Program running hold on!!\r";
  if (strlen($warn) === $iloop+1){
    $iloop = "0";
  }
  $warn = str_split($warn);
  $iloop++;
  $warn[$iloop] = "\033[35;2m\e[0m".strtoupper($warn[$iloop]);
  echo " \033[7m".implode($warn);
  usleep(90000);
}
!/usr/bin/php

如果您想使用可以在不同终端上使用的东西,而不必担心兼容性,Ncurses就是一种选择

检查:

ncurses库提供了一个强大的框架,允许程序员以文本模式创建具有视觉吸引力的用户界面


关于这一点的主要PHP文档要点是:

这是最好的答案,非常好。作为总结,该代码段的重要部分是呼应出
“\r”
(需要双引号)在写出新行之前。该函数更糟糕的是,通常以$c=0而不是$c=1开始for循环。由于以$c=0开始,函数会出现除零异常。非常重要的是:如果使用宽度太“小”的tty,这将不起作用.事实上,在这种情况下,如果长线导致一个新行,则只会移动到新行的开头,\r而不是第一行。结果是一团糟。25年的编程,我才意识到我可以用
\r
来做进度条。我刚刚发布了我的进度条:我尝试过这个,但不断添加新行。在Mac PHP CLIosx上测试使用s\r作为回车符和换行符,因此这在osx上不起作用。第3行应更改为以下内容,以避免出现来自
str_repeat
$bar=“[”($perc>0?str_repeat(=”,$perc-1):”);
谢谢@tbjers我添加了您的代码以检查0以下的输入。我将使用ceil()函数,以确保达到100%
函数进度栏($done,$total){$perc=ceil($done/$total)*100);等等。
Brilliant!我需要的是
\x1b[A
位来创建多行进度栏:)