【问题标题】:How to animate the command line?如何为命令行设置动画?
【发布时间】:2010-09-08 18:45:35
【问题描述】:

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

[======>                    ] 37%

当然加载条会移动,百分比也会发生变化,但它不会换行。我无法弄清楚如何做到这一点。有人能指出我正确的方向吗?

【问题讨论】:

    标签: command-line


    【解决方案1】:

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

    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' 也是如此。
    • @Nearoo 通常 stdout 会缓冲其输出,直到写入换行符 (\n)。冲洗使部分线条立即出现。
    【解决方案2】:

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

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

    Google 透露了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.
    }
    

    【讨论】:

    • 假设他在最新版本的 Windows(即 2000+)上运行 Win32 控制台应用程序(不是 DOS),ANSI 转义码根本不起作用。如您链接到的维基百科文章中所述。
    • 如果在 Windows 上使用 ANSI 转义序列,您可能会使用 Ansicon。 github.com/adoxa/ansicon
    【解决方案3】:

    秘诀是在行的 and 处只打印 \r 而不是 \n 或 \r\n。

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

    \n 称为换行,它将光标移动到下一行 在控制台中。如果你只使用 \r 你会覆盖之前写的行。 所以先写一行如下:

    [          ]
    

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

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

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

    \r[done      ]
    

    【讨论】:

    • 这完全有效。在我看来,它也简单得多。
    【解决方案4】:

    以下是我的回答,使用windows APIConsoles(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 定义在哪里?
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        作为Greg's answer 的后续,这里是他的功能的扩展版本,允许您显示多行消息;只需传入要显示/刷新的字符串列表或元组即可。

        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'。
        • 格雷格的工作,所以在一行它工作,但我试图做多行消息更新。 :) 我已在您的脚本中将 \n 替换为 \r\n,但仍然无法在 Windows 上运行(是吗?)。在收到一些消息后,我收到了←[A←[A,我怀疑'\x1b[A' 序列在cmd.exe 中没有做它应该做的事情。
        • @naxa '\x1b[A' 是光标向上的 ANSI 转义序列,用于将光标重置到我代码中行块的开头。我对此进行了更多调查,发现the Win32 console does not support ANSI escape sequences at all。您可能想在我的函数中添加一条 if 语句来包装 the solution mentioned here,以便在 Windows 上向标准输出添加 ANSI 支持。
        【解决方案8】:

        如果您使用脚本语言,您可以使用“tput cup”命令完成此操作... 附言据我所知,这是一个 Linux/Unix 的东西......

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-14
          • 1970-01-01
          • 2021-09-12
          • 1970-01-01
          • 1970-01-01
          • 2012-01-08
          • 1970-01-01
          • 2012-06-07
          相关资源
          最近更新 更多