Як анімувати командний рядок?


80

Я завжди дивувався, як люди оновлюють попередній рядок у командному рядку. чудовим прикладом цього є використання команди wget в linux. Він створює сортувальний рядок ASCII, який виглядає так:

[======>] 37%

і, звичайно, панель завантаження рухається і відсоток змінюється, але це не робить нового рядка. Я не можу зрозуміти, як це зробити. Хтось може вказувати мені в правильному напрямку?

Відповіді:


46

Я знаю два способи зробити це:

  • Для стирання рядка використовуйте символ повернення назад ('\ b')
  • Використовуйте cursesпакет, якщо вибрана вами мова програмування має для нього прив’язки.

А Google розкрив коди втечі 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
Якщо припустити, що він запускає консольну програму Win32 (не DOS) в останній версії Windows (тобто 2000+), ескадрирувальні коди ANSI взагалі не працюватимуть. Як зазначено у статті Вікіпедії, на яку ви посилаєтесь.
Х'ю Аллен,

Ви можете використовувати Ansicon, якщо працюєте з ANSI Escape Sequences у Windows. github.com/adoxa/ansicon
Йенс А. Кох

58

Один із способів зробити це - багаторазове оновлення рядка тексту з поточним прогресом. Наприклад:

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

Зверніть увагу, що я використовував sys.stdout.writeзамість print(це Python), оскільки printавтоматично друкує "\ r \ n" (carriage-return new-line) у кінці кожного рядка. Я просто хочу повернення каретки, яка повертає курсор на початок рядка. Крім того, flush()це необхідно, оскільки за замовчуванням sys.stdoutзмиває свої результати лише після нового рядка (або після того, як його буфер заповниться).


І те саме в 'c' з printf та '\ r'.
David L Morris,

@Nearoo Зазвичай stdout буферизує свої результати, доки не буде записано новий рядок (\ n). Промивання робить часткову лінію одразу.
Greg Hewgill

20

Секрет полягає у друкуванні лише \ r замість \ n або \ r \ n у і в рядку.

\ r називається поверненням каретки, і він рухає курсор на початку рядка

\ n називається подачею рядків, і він переміщує курсор на наступний рядок у консолі. Якщо ви використовуєте лише \ r, ви перезаписуєте раніше написаний рядок. Тож спочатку напишіть такий рядок:

[          ]

потім додайте знак для кожного кліща

\r[=         ]

\r[==        ]

...

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

і так далі. Ви можете використовувати 10 символів, кожен з яких становить 10%. Крім того, якщо ви хочете відобразити повідомлення після завершення, не забудьте також додати достатньо білих символів, щоб ви переписали раніше написані знаки рівності приблизно так:

\r[done      ]

1
Це спрацювало, повністю. На мій погляд, це набагато простіше.
Erutan409

4

нижче моя відповідь, використовуйте консолі API Windows (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;
}


3

PowerShell має командлет Write-Progress, який створює вбудовану панель прогресу, яку ви можете оновлювати та змінювати під час запуску вашого сценарію.


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

Як продовження відповіді Грега , ось розширена версія його функції, яка дозволяє відображати багаторядкові повідомлення; просто передайте список або кортеж рядків, які ви хочете відобразити / оновити.

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 Чи відповідає вам відповідь Грега (вище)? Це, швидше за все, проблема з символом нового рядка. Спробуйте замінити "\ n" на "\ r \ n".
Блейкер

Грег справді працює, тому на одному рядку це працює, але я намагався робити оновлення багаторядкових повідомлень. :) Я замінив \nна \r\nваш сценарій, але все ще не міг змусити його працювати у вікнах (правда?) Я отримую ←[A←[Aпісля деяких повідомлень, я підозрюю, що '\x1b[A'послідовність не робить те, що повинна cmd.exe.
n611x007

1
@naxa '\ x1b [A' - це екранна послідовність ANSI для курсору вгору, яка використовується для скидання курсору до початку блоку рядків у моєму коді. Я вивчив це трохи більше і виявив, що консоль Win32 взагалі не підтримує екранні послідовності ANSI . Можливо, ви захочете додати оператор if до моєї функції, щоб обернути згадане тут рішення для додавання підтримки ANSI до stdout в Windows.
Блейкер

0

Якщо ви використовуєте мову сценаріїв, ви можете скористатися командою "tput cup", щоб це зробити ... PS Це справа Linux / Unix, наскільки я знаю ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.