用 PHP 覆盖命令行输出
前几天我试图将一些输出打印到命令行,然后在之后覆盖输出。事实证明,有几种方法可以做到这一点,所以我想我会在这里详细介绍其中的一些。
到目前为止,最简单的方法是运行“clear”命令,我们可以通过system()PHP中的函数运行该命令。这将清除命令行的输出,准备好打印出您需要的任何内容。这样做的缺点是整个终端窗口都被清除了。另一个缺点是,一旦输出完成,向上滚动将显示已清除的输出。
以下将清除控制台,打印出“请稍候”,等待5秒钟,然后将其替换为“完成”。
system('clear'); echo 'Please wait'; sleep(5); system('clear'); echo 'Done';
在不移动整个屏幕的情况下替换线条是一种更好的技术。这可以使用“\r”字符来完成。您需要做的就是打印出该字符,接下来打印出的任何内容都将替换当前行。下面将与前面的示例相同,但在这种情况下,只会替换最后打印的行。
echo "Please wait"; sleep(5); echo "\rDone";
另一种方法是使用ANSI控制序列来控制光标在命令终端内的位置。为了做到这一点,我们需要打印出一个“Esc”字符(ANSI字符27),然后是ANSI指令“[0G”将光标移动到第一列。当我们打印出下一个字符串时,它会覆盖之前写入的字符串。
echo "Please wait"; sleep(5); echo chr(27) . "[0G"; echo "Done";
我们可以使用这种技术在命令行上跨多行覆盖字符。为此,我们需要计算我们打印了多少行内容,并使用ANSI控制序列移动到该行的开头并移动到输出中的第一行。
在以下示例中,我们打印出两行输出,然后休眠5秒。完成后,我们使用“[0G”ANSI控制序列将光标移动到行首,然后使用“[2A”向上移动两行。现在,当我们打印“Done”时,输出会替换之前的输出。
echo "Please wait" . PHP_EOL; echo "Working..." . PHP_EOL; sleep(5); echo chr(27) . "[0G"; echo chr(27) . "[2A"; echo "Done";
我们可以将所有这些收集到一个函数中,该函数将替换之前打印的所有输出。
function replaceCommandOutput(array $output) { static $oldLines = 0; $numNewLines = count($output) - 1; if ($oldLines == 0) { $oldLines = $numNewLines; } echo implode(PHP_EOL, $output); echo chr(27) . "[0G"; echo chr(27) . "[" . $oldLines . "A"; $numNewLines = $oldLines; }
要使用这个函数,我们需要传递一个我们想要打印的输出位数组。这是在循环中执行此操作以连续替换输出的示例。
while (true) { $output = []; $output[] = 'First Line'; $output[] = 'Time: ' . date('r'); $output[] = 'Random number: ' . rand(100, 999); $output[] = 'Random letter: ' . chr(rand(65, 89)); $output[] = 'Last Line'; replaceCommandOutput($output); usleep(100000); }
这将打印出以下内容,每0.1秒更新一次。
First Line Time: Sun, 12 Apr 2020 20:20:52 +0000 Random number: 551 Random letter: T Last Line
请注意,我尚未在Windows终端上测试过任何这些方法,因此您可能无法获得相同的效果。
您知道可用于执行此操作的任何技术吗?在下面的评论中发布它们。