跳到内容

进度条

编辑此页

当执行耗时较长的命令时,显示进度信息可能会有所帮助,进度信息会随着命令的运行而更新。

Console output showing a progress bar advance to 100%, with the estimated time left, the memory usage and a special message that changes when the bar closes completion.

注意

作为替代方案,请考虑使用 SymfonyStyle 来显示进度条。

要显示进度详情,请使用 ProgressBar,传递总单元数,并在命令执行时推进进度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Symfony\Component\Console\Helper\ProgressBar;

// creates a new progress bar (50 units)
$progressBar = new ProgressBar($output, 50);

// starts and displays the progress bar
$progressBar->start();

$i = 0;
while ($i++ < 50) {
    // ... do some work

    // advances the progress bar 1 unit
    $progressBar->advance();

    // you can also advance the progress bar by more than 1 unit
    // $progressBar->advance(3);
}

// ensures that the progress bar is at 100%
$progressBar->finish();

提示

您也可以通过使用负值调用 $progress->advance() 来回退进度条(即向后退步)。例如,如果您调用 $progress->advance(-2),则进度条将回退 2 步。

注意

默认情况下,进度条助手使用错误输出 (stderr) 作为其默认输出。可以通过将 StreamOutput 的实例传递给 ProgressBar 构造函数来更改此行为。

除了通过步数推进进度条(使用 advance() 方法),您还可以通过调用 setProgress() 方法来设置当前进度。

如果您要恢复长时间运行的任务,最好从某个特定点开始绘制进度条。使用 start() 的第二个可选参数来设置起始点。

1
2
3
4
5
6
7
use Symfony\Component\Console\Helper\ProgressBar;

// creates a new progress bar (100 units)
$progressBar = new ProgressBar($output, 100);

// displays the progress bar starting at 25 completed units
$progressBar->start(null, 25);

提示

如果您的平台不支持 ANSI 代码,则进度条的更新将作为新行添加。为防止输出被淹没,请使用 minSecondsBetweenRedraws() 方法来限制重绘次数,并使用 setRedrawFrequency() 方法来每 N 次迭代重绘一次。默认情况下,重绘频率为 100 毫秒max 值的 10%

如果您事先不知道确切的步数,请将其设置为合理的值,然后调用 setMaxSteps() 方法根据需要进行更新。

1
2
3
4
5
// start with a 50 units progressbar
$progressBar = new ProgressBar($output, 50);

// a complex task has just been created: increase the progressbar to 200 units
$progressBar->setMaxSteps(200);

另一种解决方案是在创建 ProgressBar 实例时省略 steps 参数。

1
$progressBar = new ProgressBar($output);

然后,进度将显示为指示器。

1
2
3
4
5
6
7
8
9
# no max steps (displays it like a throbber)
    0 [>---------------------------]
    5 [----->----------------------]
    5 [============================]

# max steps defined
 0/3 [>---------------------------]   0%
 1/3 [=========>------------------]  33%
 3/3 [============================] 100%

提示

另一种替代方案是使用进度指示器而不是进度条。

无论何时完成任务,都不要忘记调用 finish() 以确保进度条显示刷新为 100% 完成。

注意

如果您想在进度条运行时输出某些内容,请先调用 clear()。完成后,调用 display() 以再次显示进度条。

如果进度信息存储在可迭代变量中(例如数组或 PHP 生成器),则可以使用 iterate() 方法,该方法会自动启动、推进和完成进度条。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Console\Helper\ProgressBar;

$progressBar = new ProgressBar($output);

// $iterable can be array
$iterable = [1, 2];
foreach ($progressBar->iterate($iterable) as $value) {
    // ... do some work
}

// or a generator
function iterable() { yield 1; yield 2; ... };
foreach ($progressBar->iterate(iterable()) as $value) {
    // ... do some work
}

前面的代码将输出:

1
2
3
0/2 [>---------------------------]   0%
1/2 [==============>-------------]  50%
2/2 [============================] 100%

自定义进度条

内置格式

默认情况下,进度条上呈现的信息取决于 OutputInterface 实例的当前详细级别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# OutputInterface::VERBOSITY_NORMAL (CLI with no verbosity flag)
 0/3 [>---------------------------]   0%
 1/3 [=========>------------------]  33%
 3/3 [============================] 100%

# OutputInterface::VERBOSITY_VERBOSE (-v)
 0/3 [>---------------------------]   0%  1 sec
 1/3 [=========>------------------]  33%  1 sec
 3/3 [============================] 100%  1 sec

# OutputInterface::VERBOSITY_VERY_VERBOSE (-vv)
 0/3 [>---------------------------]   0%  1 sec/1 sec
 1/3 [=========>------------------]  33%  1 sec/1 sec
 3/3 [============================] 100%  1 sec/1 sec

# OutputInterface::VERBOSITY_DEBUG (-vvv)
 0/3 [>---------------------------]   0%  1 sec/1 sec  1.0 MB
 1/3 [=========>------------------]  33%  1 sec/1 sec  1.0 MB
 3/3 [============================] 100%  1 sec/1 sec  1.0 MB

注意

如果您使用 quiet 标志 (-q) 调用命令,则不会显示进度条。

除了依赖当前命令的详细模式外,您还可以通过 setFormat() 强制使用格式。

1
$progressBar->setFormat('verbose');

内置格式如下:

  • normal
  • verbose
  • very_verbose
  • debug

如果您没有为进度条设置步数,请使用 _nomax 变体:

  • normal_nomax
  • verbose_nomax
  • very_verbose_nomax
  • debug_nomax

自定义格式

除了使用内置格式外,您还可以设置自己的格式。

1
$progressBar->setFormat('%bar%');

这将格式设置为仅显示进度条本身。

1
2
3
>---------------------------
=========>------------------
============================

进度条格式是一个字符串,其中包含特定的占位符(用 % 字符括起来的名称);占位符根据进度条的当前进度进行替换。以下是内置占位符的列表:

  • current:当前步骤;
  • max:最大步数(如果未定义最大值,则为 0);
  • bar:进度条本身;
  • percent:完成百分比(如果未定义最大值,则不可用);
  • elapsed:自进度条开始以来经过的时间;
  • remaining:完成任务的剩余时间(如果未定义最大值,则不可用);
  • estimated:完成任务的估计时间(如果未定义最大值,则不可用);
  • memory:当前内存使用量;
  • message:用于在进度条中显示任意消息(稍后说明)。

时间字段 elapsedremainingestimated 的精度为 2。这意味着 172799 秒显示为 1 天 23 小时,而不是 1 天 23 小时 59 分 59 秒

例如,以下是如何将格式设置为与 debug 格式相同:

1
$progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:16s%/%estimated:-16s% %memory:6s%');

注意到添加到某些占位符的 :6s 部分了吗?这就是您可以调整进度条外观(格式和对齐方式)的方式。冒号 (:) 后的部分用于设置字符串的 sprintf 格式。

除了为进度条的给定实例设置格式外,您还可以定义全局格式。

1
2
3
4
ProgressBar::setFormatDefinition('minimal', 'Progress: %percent%%');

$progressBar = new ProgressBar($output, 3);
$progressBar->setFormat('minimal');

此代码定义了一个新的 minimal 格式,然后您可以将其用于进度条。

1
2
3
Progress: 0%
Progress: 33%
Progress: 100%

提示

几乎总是最好重新定义内置格式,而不是创建新格式,因为这样可以使显示根据命令的详细程度标志自动变化。

当定义包含仅在已知最大步数时才可用的占位符的新样式时,您应该创建一个 _nomax 变体。

1
2
3
4
5
ProgressBar::setFormatDefinition('minimal', '%percent%% %remaining%');
ProgressBar::setFormatDefinition('minimal_nomax', '%percent%%');

$progressBar = new ProgressBar($output);
$progressBar->setFormat('minimal');

显示进度条时,如果进度条没有最大步数(如上面的示例所示),格式将自动设置为 minimal_nomax

提示

格式可以包含任何有效的 ANSI 代码,也可以使用 Symfony 特定的方式来设置颜色。

1
2
3
4
ProgressBar::setFormatDefinition(
    'minimal',
    '<info>%percent%</info>\033[32m%\033[0m <fg=white;bg=blue>%remaining%</>'
);

注意

格式可以跨越多行;当您想要在进度条旁边显示更多上下文信息时,这非常有用(请参阅本文开头的示例)。

进度条设置

在占位符中,bar 有点特殊,因为用于显示它的所有字符都可以自定义。

1
2
3
4
5
6
7
8
9
10
11
// the finished part of the bar
$progressBar->setBarCharacter('<comment>=</comment>');

// the unfinished part of the bar
$progressBar->setEmptyBarCharacter(' ');

// the progress character
$progressBar->setProgressCharacter('|');

// the bar width
$progressBar->setBarWidth(50);

警告

出于性能原因,Symfony 每 100 毫秒重绘一次屏幕。如果对于您的应用程序来说速度太快或太慢,请使用方法 minSecondsBetweenRedraws()maxSecondsBetweenRedraws()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$progressBar = new ProgressBar($output, 50000);
$progressBar->start();

// this redraws the screen every 100 iterations, but sets additional limits:
// don't redraw slower than 200ms (0.2) or faster than 100ms (0.1)
$progressBar->setRedrawFrequency(100);
$progressBar->maxSecondsBetweenRedraws(0.2);
$progressBar->minSecondsBetweenRedraws(0.1);

$i = 0;
while ($i++ < 50000) {
    // ... do some work

    $progressBar->advance();
}

自定义占位符

如果您想显示一些依赖于进度条显示的信息,而这些信息在内置占位符列表中不可用,您可以创建自己的占位符。让我们看看如何创建一个 remaining_steps 占位符来显示剩余步数。

1
2
3
4
5
6
7
// This definition is globally registered for all ProgressBar instances
ProgressBar::setPlaceholderFormatterDefinition(
    'remaining_steps',
    function (ProgressBar $progressBar, OutputInterface $output): int {
        return $progressBar->getMaxSteps() - $progressBar->getProgress();
    }
);

也可以使用 setPlaceholderFormatter 方法为每个 ProgressBar 实例设置占位符格式化程序。

1
2
3
4
5
$progressBar = new ProgressBar($output, 3, 0);
$progressBar->setFormat('%countdown% [%bar%]');
$progressBar->setPlaceholderFormatter('countdown', function (ProgressBar $progressBar) {
    return $progressBar->getMaxSteps() - $progressBar->getProgress();
});

自定义消息

进度条定义了一个名为 message 的占位符来显示任意消息。但是,没有内置格式包含该占位符,因此在显示这些消息之前,您必须定义自己的自定义格式。

1
2
3
4
ProgressBar::setFormatDefinition('custom', ' %current%/%max% -- %message%');

$progressBar = new ProgressBar($output, 100);
$progressBar->setFormat('custom');

现在,在显示进度条之前,使用 setMessage() 方法设置 %message% 占位符的值。

1
2
3
4
5
6
7
8
// ...
$progressBar->setMessage('Start');
$progressBar->start();
// 0/100 -- Start

$progressBar->setMessage('Task is in progress...');
$progressBar->advance();
// 1/100 -- Task is in progress...

消息也可以与自定义占位符组合使用。在此示例中,进度条使用 %message%%filename% 占位符。

1
2
3
4
ProgressBar::setFormatDefinition('custom', ' %current%/%max% -- %message% (%filename%)');

$progressBar = new ProgressBar($output, 100);
$progressBar->setFormat('custom');

setMessage() 方法接受第二个可选参数,用于设置自定义占位符的值。

1
2
3
4
5
6
7
8
// ...
// $files = ['client-001/invoices.xml', '...'];
foreach ($files as $filename) {
    $progressBar->setMessage('Importing invoices...');
    $progressBar->setMessage($filename, 'filename');
    $progressBar->advance();
    // 2/100 -- Importing invoices... (client-001/invoices.xml)
}

显示多个进度条

当使用控制台输出节时,可以同时显示多个进度条并独立更改它们的进度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$section1 = $output->section();
$section2 = $output->section();

$progress1 = new ProgressBar($section1);
$progress2 = new ProgressBar($section2);

$progress1->start(100);
$progress2->start(100);

$i = 0;
while (++$i < 100) {
    $progress1->advance();

    if ($i % 2 === 0) {
        $progress2->advance(4);
    }

    usleep(50000);
}

经过几次迭代后,终端中的输出将如下所示:

1
2
34/100 [=========>------------------]  34%
68/100 [===================>--------]  68%
本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本