Frage Wie animiere ich die Befehlszeile?


Ich habe mich immer gefragt, wie Leute eine vorherige Zeile in einer Befehlszeile aktualisieren. Ein gutes Beispiel hierfür ist die Verwendung des Befehls wget in Linux. Es erstellt eine Art ASCII-Ladebalken, der wie folgt aussieht:

[======>] 37%

und natürlich bewegt sich der Ladebalken und der Prozentsatz ändert sich, aber es wird keine neue Zeile erstellt. Ich kann nicht herausfinden, wie das geht. Kann mir jemand in die richtige Richtung zeigen?


76
2017-09-13 00:53


Ursprung


Antworten:


Es gibt zwei Möglichkeiten, dies zu tun:

  • Verwenden Sie das Rücksprungzeichen ('\ b'), um Ihre Zeile zu löschen
  • Benutze die curses Paket, wenn Ihre Programmiersprache der Wahl Bindings dafür hat.

Und ein Google enthüllt ANSI-Escape-Codes, die scheinen ein guter Weg zu sein. Zur Referenz hier eine Funktion in 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.
}

42
2017-09-13 00:56



Eine Möglichkeit ist das wiederholte Aktualisieren der Textzeile mit dem aktuellen Fortschritt. Beispielsweise:

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

Beachten Sie, dass ich verwendet habe sys.stdout.write Anstatt von print (Das ist Python), weil print wird am Ende jeder Zeile automatisch "\ r \ n" (Wagenrücklauf-Zeilenumbruch) gedruckt. Ich möchte nur den Wagenrücklauf, der den Cursor an den Anfang der Zeile zurückführt. Auch der flush() ist notwendig, weil standardmäßig sys.stdout Leert nur die Ausgabe nach einer neuen Zeile (oder nachdem der Puffer voll ist).


56
2017-09-13 00:58



Das Geheimnis ist, nur \ r anstelle von \ n oder \ r \ n am und der Zeile zu drucken.

\ r heißt Wagenrücklauf und bewegt den Cursor am Anfang der Zeile

\ n heißt Zeilenvorschub und bewegt den Cursor auf die nächste Zeile In der Konsole. Wenn Sie nur \ r verwenden, überschreiben Sie die zuvor geschriebene Zeile. Schreiben Sie also zuerst eine Zeile wie die folgende:

[          ]

Fügen Sie dann für jeden Tick ein Zeichen hinzu

\r[=         ]

\r[==        ]

...

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

und so weiter. Sie können 10 Zeichen verwenden, die jeweils 10% repräsentieren. Wenn Sie nach Abschluss eine Nachricht anzeigen möchten, vergessen Sie nicht, auch so viele weiße Zeichen hinzuzufügen, dass Sie die zuvor geschriebenen Gleichheitszeichen wie folgt überschreiben:

\r[done      ]

13
2017-10-06 15:04



Unten ist meine Antwort, benutze die Windows APIKonsolen (Windows), Codierung von 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;
}

4
2017-12-06 06:47



PowerShell verfügt über ein Cmdlet Write-Progress, mit dem eine Fortschrittsleiste in der Konsole erstellt wird, die Sie bei der Ausführung des Skripts aktualisieren und ändern können.


3
2017-09-13 01:10



Hier ist die Antwort auf Ihre Frage ... (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")

3
2017-07-04 09:12



Als Nachfolger zu Gregs Antwort, hier ist eine erweiterte Version seiner Funktion, mit der Sie mehrzeilige Nachrichten anzeigen können; übergeben Sie einfach eine Liste oder ein Tupel der Zeichenfolgen, die Sie anzeigen / aktualisieren möchten.

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()

Hinweis: Ich habe dies nur mit einem Linux-Terminal getestet, sodass Ihre Laufleistung auf Windows-basierten Systemen variieren kann.


2
2018-06-23 22:44



Wenn Sie eine Skriptsprache verwenden, können Sie den Befehl "tput cup" verwenden, um dies zu erreichen ... P.S. Dies ist eine Linux / Unix-Sache nur soweit ich weiß ...


0
2017-10-24 18:55