Câu hỏi Làm thế nào để animate dòng lệnh?


Tôi đã luôn luôn tự hỏi làm thế nào mọi người cập nhật một dòng trước đó trong một dòng lệnh. một ví dụ tuyệt vời của việc này là khi sử dụng lệnh wget trong linux. Nó tạo ra một thanh nạp ASCII có dạng như sau:

[======>] 37%

và tất nhiên thanh tải di chuyển và phần trăm thay đổi, nhưng nó không tạo ra một dòng mới. Tôi không thể tìm ra cách để làm điều này. Ai đó có thể chỉ cho tôi đi đúng hướng?


76
2017-09-13 00:53


gốc




Các câu trả lời:


Có hai cách tôi biết để làm điều này:

  • Sử dụng ký tự thoát backspace ('\ b') để xóa dòng của bạn
  • Sử dụng curses gói, nếu ngôn ngữ lập trình của bạn lựa chọn có ràng buộc cho nó.

Và Google tiết lộ Mã thoát ANSI, dường như là một cách tốt. Để tham khảo, đây là một hàm trong C ++ để làm điều này:

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



Giả sử anh ta đang chạy một ứng dụng giao diện điều khiển Win32 (không phải DOS) trên một phiên bản Windows gần đây (tức là 2000+), các mã thoát ANSI sẽ không hoạt động chút nào. Như đã nêu trong bài viết wikipedia bạn đã liên kết đến. - Hugh Allen
Bạn có thể sử dụng Ansicon, nếu làm việc với ANSI Escape Sequences trên Windows. github.com/adoxa/ansicon - Jens A. Koch


Một cách để làm điều này là liên tục cập nhật dòng văn bản với tiến trình hiện tại. Ví dụ:

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

Lưu ý rằng tôi đã sử dụng sys.stdout.write thay vì print (đây là Python) vì print tự động in "\ r \ n" (dòng vận chuyển trở lại dòng mới) ở cuối mỗi dòng. Tôi chỉ muốn trả về vận chuyển, trả về con trỏ đến đầu dòng. Ngoài ra, flush() là cần thiết vì theo mặc định, sys.stdout chỉ flushes đầu ra của nó sau khi một newline (hoặc sau khi bộ đệm của nó được đầy đủ).


56
2017-09-13 00:58



Và tương tự trong 'c' với printf và '\ r'. - David L Morris
Chính xác là "đỏ mặt" sau flush()? Chỉ tò mò thôi ... - Nearoo
@Nearoo Bình thường stdout đệm đầu ra của nó cho đến khi một dòng mới (\ n) được viết. Flushing làm cho một phần dòng xuất hiện ngay lập tức. - Greg Hewgill
@GregHewgill Làm cho cảm giác, cảm ơn! - Nearoo


Bí quyết là chỉ in \ r thay vì \ n hoặc \ r \ n tại và của dòng.

\ r được gọi là vận chuyển trở lại và nó di chuyển con trỏ ở đầu dòng

\ n được gọi là nguồn cấp dữ liệu dòng và nó di chuyển con trỏ trên dòng tiếp theo Trong giao diện điều khiển. Nếu bạn chỉ sử dụng \ r bạn ghi đè lên dòng văn bản trước đó. Vì vậy, đầu tiên viết một dòng như sau:

[          ]

sau đó thêm một dấu hiệu cho mỗi đánh dấu

\r[=         ]

\r[==        ]

...

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

và vân vân. Bạn có thể sử dụng 10 ký tự, mỗi ký tự đại diện cho 10%. Ngoài ra, nếu bạn muốn hiển thị thông báo khi hoàn tất, đừng quên thêm đủ các ký tự trắng để bạn ghi đè lên các ký hiệu bằng văn bản trước đó như sau:

\r[done      ]

13
2017-10-06 15:04



Điều này làm việc, hoàn toàn. Đó là MUCH đơn giản hơn trong quan điểm của tôi, quá. - Erutan409


dưới đây là câu trả lời của tôi, sử dụng API cửa sổBảng điều khiển (Windows), mã hóa của 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



Ở đâu bInfo được xác định? - Tomáš Zato


PowerShell có một lệnh ghép ngắn Viết-Tiến trình tạo thanh tiến trình trong bảng điều khiển mà bạn có thể cập nhật và sửa đổi khi tập lệnh của bạn chạy.


3
2017-09-13 01:10





Đây là câu trả lời cho câu hỏi của bạn ... (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





Theo dõi Câu trả lời của Greg, đây là một phiên bản mở rộng của chức năng của mình cho phép bạn hiển thị thông điệp nhiều dòng; chỉ cần chuyển vào một danh sách hoặc một chuỗi các chuỗi bạn muốn hiển thị / làm mới.

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

Lưu ý: Tôi đã chỉ thử nghiệm điều này bằng cách sử dụng một thiết bị đầu cuối Linux, vì vậy số dặm của bạn có thể khác nhau trên các hệ thống dựa trên Windows.


2
2018-06-23 22:44



Windows (7sp1x64): không hoạt động. - n611x007
@naxa Câu trả lời của Greg (ở trên) có phù hợp với bạn không? Nó rất có thể là một vấn đề với ký tự dòng mới. Thử thay thế '\ n' bằng '\ r \ n'. - Blaker
Greg làm việc, do đó, trên một dòng nó hoạt động, nhưng tôi đã cố gắng để cập nhật tin nhắn đa dòng. :) Tôi đã thay thế \n đến \r\n trong kịch bản của bạn, nhưng vẫn không thể làm cho nó hoạt động trên các cửa sổ (phải không?). Tôi đã nhận được ←[A←[A sau một số tin nhắn, tôi nghi ngờ '\x1b[A' trình tự không làm những gì cần cmd.exe. - n611x007
@naxa The '\ x1b [A' là một chuỗi thoát ANSI cho con trỏ lên, được sử dụng để đặt lại con trỏ đến đầu khối của các dòng trong mã của tôi. Tôi nhìn vào nó nhiều hơn một chút và thấy rằng giao diện điều khiển Win32 không hỗ trợ các chuỗi thoát ANSI. Bạn có thể muốn thêm câu lệnh if vào hàm của tôi để bọc giải pháp được đề cập ở đây để thêm hỗ trợ ANSI cho stdout trên Windows. - Blaker


Nếu bạn sử dụng một ngôn ngữ kịch bản, bạn có thể sử dụng lệnh "tput cup" để thực hiện việc này ... P.S. Đây là một điều Linux / Unix chỉ theo như tôi biết ...


0
2017-10-24 18:55