我一直想知道人们如何在命令行中更新前一行。一个很好的例子是在 linux 中使用 wget 命令。它创建了一个 ASCII 加载条,如下所示:
[======>] 37%
当然,加载条会移动,百分比也会发生变化,但它不会换行。我无法弄清楚如何做到这一点。有人可以指出我正确的方向吗?
我一直想知道人们如何在命令行中更新前一行。一个很好的例子是在 linux 中使用 wget 命令。它创建了一个 ASCII 加载条,如下所示:
[======>] 37%
当然,加载条会移动,百分比也会发生变化,但它不会换行。我无法弄清楚如何做到这一点。有人可以指出我正确的方向吗?
一种方法是使用当前进度重复更新文本行。例如:
def status(percent):
sys.stdout.write("%3d%%\r" % percent)
sys.stdout.flush()
请注意,我使用sys.stdout.write
而不是print
(这是 Python),因为print
在每行的末尾自动打印“\r\n”(回车换行符)。我只想要将光标返回到行首的回车符。此外,这flush()
是必要的,因为默认情况下,sys.stdout
仅在换行符之后(或在其缓冲区已满之后)刷新其输出。
我知道有两种方法可以做到这一点:
curses
如果您选择的编程语言有绑定,请使用该包。谷歌透露了ANSI Escape Codes,这似乎是一个好方法。作为参考,这是 C++ 中的一个函数来执行此操作:
void DrawProgressBar(int len, double percent) {
cout << "\x1B[2K"; // Erase the entire current line.
cout << "\x1B[0E"; // Move to the beginning of the current line.
string progress;
for (int i = 0; i < len; ++i) {
if (i < static_cast<int>(len * percent)) {
progress += "=";
} else {
progress += " ";
}
}
cout << "[" << progress << "] " << (static_cast<int>(100 * percent)) << "%";
flush(cout); // Required.
}
秘诀是在行的 and 处只打印 \r 而不是 \n 或 \r\n。
\r 称为回车,它将光标移动到行首
\n 称为换行符,它将光标移动到控制台中的下一行。如果你只使用 \r 你会覆盖之前写的行。所以首先写一行如下:
[ ]
然后为每个刻度添加一个符号
\r[= ]
\r[== ]
...
\r[==========]
等等。您可以使用 10 个字符,每个字符代表 10%。此外,如果您想在完成后显示一条消息,请不要忘记添加足够的白色字符,以便覆盖之前写入的等号,如下所示:
\r[done ]
下面是我的答案,使用 windows API Consoles(Windows),C 编码。
/*
* file: ProgressBarConsole.cpp
* description: a console progress bar Demo
* author: lijian <hustlijian@gmail.com>
* version: 1.0
* date: 2012-12-06
*/
#include <stdio.h>
#include <windows.h>
HANDLE hOut;
CONSOLE_SCREEN_BUFFER_INFO bInfo;
char charProgress[80] =
{"================================================================"};
char spaceProgress = ' ';
/*
* show a progress in the [row] line
* row start from 0 to the end
*/
int ProgressBar(char *task, int row, int progress)
{
char str[100];
int len, barLen,progressLen;
COORD crStart, crCurr;
GetConsoleScreenBufferInfo(hOut, &bInfo);
crCurr = bInfo.dwCursorPosition; //the old position
len = bInfo.dwMaximumWindowSize.X;
barLen = len - 17;//minus the extra char
progressLen = (int)((progress/100.0)*barLen);
crStart.X = 0;
crStart.Y = row;
sprintf(str,"%-10s[%-.*s>%*c]%3d%%", task,progressLen,charProgress, barLen-progressLen,spaceProgress,50);
#if 0 //use stdand libary
SetConsoleCursorPosition(hOut, crStart);
printf("%s\n", str);
#else
WriteConsoleOutputCharacter(hOut, str, len,crStart,NULL);
#endif
SetConsoleCursorPosition(hOut, crCurr);
return 0;
}
int main(int argc, char* argv[])
{
int i;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hOut, &bInfo);
for (i=0;i<100;i++)
{
ProgressBar("test", 0, i);
Sleep(50);
}
return 0;
}
PowerShell 有一个 Write-Progress cmdlet,它创建一个控制台内进度条,您可以在脚本运行时更新和修改该进度条。
这是您问题的答案...(python)
def disp_status(timelapse, timeout):
if timelapse and timeout:
percent = 100 * (float(timelapse)/float(timeout))
sys.stdout.write("progress : ["+"*"*int(percent)+" "*(100-int(percent-1))+"]"+str(percent)+" %")
sys.stdout.flush()
stdout.write("\r \r")
作为Greg 回答的后续,这里是他的功能的扩展版本,允许您显示多行消息;只需传入要显示/刷新的字符串列表或元组。
def status(msgs):
assert isinstance(msgs, (list, tuple))
sys.stdout.write(''.join(msg + '\n' for msg in msgs[:-1]) + msgs[-1] + ('\x1b[A' * (len(msgs) - 1)) + '\r')
sys.stdout.flush()
注意:我只使用 linux 终端对此进行了测试,因此您的里程在基于 Windows 的系统上可能会有所不同。
如果您使用脚本语言,您可以使用“tput cup”命令来完成这项工作...... PS 这是一个 Linux/Unix 的东西,据我所知......