如何为命令行设置动画?


80

我一直想知道人们如何在命令行中更新上一行。一个很好的例子是在Linux中使用wget命令时。它会创建如下所示的ASCII加载条:

[======>] 37%

当然,加载栏会移动,百分比也会发生变化,但是不会产生新的变化。我不知道该怎么做。有人可以指出我正确的方向吗?

Answers:


46

我知道有两种方法可以做到这一点:

  • 使用退格转义符('\ b')删除行
  • curses如果您选择的编程语言具有绑定,请使用该包。

谷歌透露了ANSI转义码,这似乎是个好方法。作为参考,下面是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.
}

7
假设他正在Windows的最新版本(即2000+)上运行Win32控制台应用程序(而不是DOS),则ANSI转义代码将完全无效。如您链接到的维基百科文章所述。
休·艾伦

如果在Windows上使用ANSI转义序列,则可以使用Ansicon。github.com/adoxa/ansicon
Jens A. Koch

58

一种方法是使用当前进度重复更新文本行。例如:

def status(percent):
    sys.stdout.write("%3d%%\r" % percent)
    sys.stdout.flush()

请注意,我之所以使用sys.stdout.write而不是print(这是Python),是因为print在每行末尾会自动打印“ \ r \ n”(回车换行)。我只想要回车,它将光标返回到行的开头。另外,这flush()是必需的,因为默认情况下,sys.stdout仅在换行符之后(或其缓冲区已满)刷新其输出。


与'c'中的printf和'\ r'相同。
David L Morris

@Nearoo通常,stdout缓冲其输出,直到写入换行符(\ n)。冲洗会使部分线条立即出现。
Greg Hewgill '17

20

秘密是仅在该行的和处打印\ r而不是\ n或\ r \ n。

\ r称为回车,它将光标移动到行首

\ n称为换行,它将光标移至控制台的下一行。如果仅使用\ r,则将覆盖先前编写的行。因此,首先编写如下代码:

[          ]

然后为每个刻度线添加一个符号

\r[=         ]

\r[==        ]

...

\r[==========]

等等。您可以使用10个字符,每个字符占10%。另外,如果您想在完成后显示一条消息,请不要忘记添加足够的白色字符,以便覆盖以前写的等号,如下所示:

\r[done      ]

1
完全有效。我认为这也更简单。
Erutan409

4

以下是我的答案,请使用Windows API控制台(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;
}

在哪里bInfo定义?
托马什Zato -恢复莫妮卡

3

PowerShell具有一个Write-Progress cmdlet,该cmdlet创建一个控制台中进度条,您可以在脚本运行时对其进行更新和修改。


3

这是您问题的答案...(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")

2

作为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的系统上有所不同。


@naxa Greg的答案(以上)对您有用吗?换行符很可能是一个问题。尝试将'\ n'替换为'\ r \ n'。
Blaker 2013年

格雷格(Greg)确实可以工作,所以一方面可以工作,但我尝试进行多行消息更新。:)我已在您的脚本中替换\n\r\n,但仍然无法在Windows上运行它(对吗?)。我收到了←[A←[A一些消息,我怀疑该'\x1b[A'序列没有执行应有的操作cmd.exe
n611x007 2013年

1
@naxa'\ x1b [A'是一个用于游标向上的ANSI转义序列,用于将游标重置为我的代码行的开头。我仔细研究了一下,发现Win32控制台根本不支持ANSI转义序列。您可能想在我的函数中添加一个if语句,以包装此处提到的在Windows上向stdout添加ANSI支持的解决方案
Blaker 2013年

0

如果您使用脚本语言,则可以使用“ tput cup”命令来完成此操作... PS据我所知,这是Linux / Unix方面的内容...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.