2008-09-13 37 views
74

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 tải ASCII của các loại trông như thế này:Làm thế nào để animate dòng lệnh?

[======>                                        ] 37%

và tất nhiên loa ding bar 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?

Trả lời

42

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

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

Và Google tiết lộ ANSI Escape Codes, có vẻ là một cách hay. Để 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. 
} 
+7

Giả sử anh ấy chạy một giao diện điều khiển ứng dụng Win32 (không 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. Như đã nêu trong bài viết wikipedia bạn đã liên kết đến. –

+0

Bạn có thể sử dụng Ansicon, nếu làm việc với ANSI Escape Sequences trên Windows. https://github.com/adoxa/ansicon –

56

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" (vận chuyển-trở lại mới-line) vào 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 bởi vì theo mặc định, sys.stdout chỉ flushes đầu ra của nó sau khi một dòng mới (hoặc sau khi bộ đệm của nó được đầy đủ).

+0

Và tương tự trong 'c' với printf và '\ r'. –

+0

Chính xác thì "đỏ ửng" sau 'flush()' là gì? Chỉ cần tò mò ... – Nearoo

+0

@Nearoo Thông 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. –

3

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.

0

Nếu bạn sử dụng 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 ...

2

Theo dõi tới Greg's answer, đây là 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 thiết bị đầu cuối Linux, vì vậy số dặm của bạn có thể thay đổi trên hệ thống dựa trên Windows.

+0

Windows (7sp1x64): không hoạt động. – n611x007

+0

@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

+0

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' thành' \ r \ n' trong tập lệnh của bạn, nhưng vẫn không thể làm việc trên các cửa sổ (phải không?). Tôi đã nhận được '← [A ← [A' sau khi một số tin nhắn, tôi nghi ngờ trình tự' '\ x1b [A'' không làm gì trong 'cmd.exe'. – n611x007

3

Dưới đâ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") 
4

dưới đây là câu trả lời của tôi, sử dụng các cửa sổ API Consoles(Windows), mã hóa của C.

/* 
* file: ProgressBarConsole.cpp 
* description: a console progress bar Demo 
* author: lijian <[email protected]> 
* 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; 
} 
+0

'bInfo' được định nghĩa ở đâu? –

13

Bí quyết 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ỏ vào đầu dòng

\ n được gọi là thức ăn đườ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 tick

\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ị một thông báo khi hoàn thành, đừng quên còn thêm đủ ký tự trắng để bạn ghi đè lên dấu bằng văn bản trước đây như vậy:

\r[done  ] 
+1

Đ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

Các vấn đề liên quan