명령 줄을 애니메이션하는 방법은 무엇입니까?


80

나는 사람들이 명령 줄에서 이전 줄을 어떻게 업데이트하는지 항상 궁금해했습니다. 이에 대한 좋은 예는 Linux에서 wget 명령을 사용할 때입니다. 다음과 같은 ASCII 로딩 바를 생성합니다.

[======>] 37 %

물론 로딩 바가 움직이고 퍼센트가 변경되지만 새로운 라인을 만들지는 않습니다. 이 작업을 수행하는 방법을 알 수 없습니다. 누군가 나를 올바른 방향으로 가리킬 수 있습니까?

답변:


46

이 작업을 수행하는 방법에는 두 가지가 있습니다.

  • 백 스페이스 이스케이프 문자 ( '\ b')를 사용하여 줄을 지 웁니다.
  • curses선택한 프로그래밍 언어에 바인딩이있는 경우 패키지를 사용하십시오 .

그리고 구글 은 좋은 방법 인 것처럼 보이는 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
최신 버전의 Windows (예 : 2000+)에서 Win32 콘솔 앱 (DOS가 아님)을 실행하고 있다고 가정하면 ANSI 이스케이프 코드는 전혀 작동하지 않습니다. 링크 한 위키피디아 기사에 명시된대로.
Hugh Allen

Windows에서 ANSI 이스케이프 시퀀스로 작업하는 경우 Ansicon을 사용할 수 있습니다. github.com/adoxa/ansicon
옌스 A. 코흐

58

이를 수행하는 한 가지 방법은 현재 진행 상황으로 텍스트 줄을 반복적으로 업데이트하는 것입니다. 예를 들면 :

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

각 줄 끝에 "\ r \ n"(carriage-return new-line)을 자동으로 인쇄 하기 때문에 (이것은 Python) sys.stdout.write대신 사용 했습니다 . 커서를 줄의 시작 부분으로 되 돌리는 캐리지 리턴을 원합니다. 또한,은 기본적으로 있기 때문에 필요한 경우에만 줄 바꿈 후 (또는 버퍼가 가득 차면 후)의 출력을 플러시합니다.printprintflush()sys.stdout


그리고 'c'에서 printf와 '\ r'과 동일합니다.
David L Morris

@Nearoo 일반적으로 stdout은 줄 바꿈 (\ n)이 작성 될 때까지 출력을 버퍼링합니다. 플러싱은 부분 선이 즉시 표시되도록합니다.
Greg Hewgill

20

비밀은 줄 및 줄에 \ n 또는 \ r \ n 대신 \ r 만 인쇄하는 것입니다.

\ r 캐리지 리턴이라고하며 줄의 시작 부분에서 커서를 이동합니다.

\ n은 줄 바꿈이라고하며 콘솔에서 커서를 다음 줄로 이동합니다. \ r 만 사용하면 이전에 작성된 줄을 덮어 씁니다. 따라서 먼저 다음과 같은 줄을 작성하십시오.

[          ]

그런 다음 각 진드기에 대한 기호를 추가하십시오

\r[=         ]

\r[==        ]

...

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

등등. 각각 10 %를 나타내는 10 개의 문자를 사용할 수 있습니다. 또한 완료되었을 때 메시지를 표시하려면 이전에 작성된 등호를 다음과 같이 덮어 쓸 수 있도록 충분한 흰색 문자를 추가하는 것을 잊지 마십시오.

\r[done      ]

1
이것은 완전히 작동했습니다. 제 생각에는 훨씬 더 간단합니다.
Erutan409 2015 년

4

아래는 내 대답입니다 .Windows API Consoles (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정의되어 있습니까?
토마스 Zato - 분석 재개 모니카

3

PowerShell에는 스크립트가 실행될 때 업데이트하고 수정할 수있는 콘솔 내 진행률 표시 줄을 만드는 Write-Progress cmdlet이 있습니다.


3

귀하의 질문에 대한 답변입니다 ... (파이썬)

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

Greg의 대답에 대한 후속으로 , 여기에 여러 줄 메시지를 표시 할 수있는 그의 기능의 확장 버전이 있습니다. 표시 / 새로 고침하려는 문자열의 목록 또는 튜플을 전달하기 만하면됩니다.

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'으로 바꾸십시오.
Blaker 2013 년

Greg는 작업을 수행하므로 한 줄에서 작동하지만 여러 줄 메시지 업데이트를 시도했습니다. :) 내가 대체 한 \n\r\n이 윈도우에서 작동 가져올 수 없습니다 아직 스크립트에서,하지만 (당신은?). ←[A←[A몇 가지 메시지를 받고 있는데 '\x1b[A'시퀀스가 cmd.exe.
n611x007 2013 년

1
@naxa '\ x1b [A'는 커서 위로를위한 ANSI 이스케이프 시퀀스로, 커서를 코드에서 줄의 시작 부분으로 재설정하는 데 사용됩니다. 나는 이것을 좀 더 조사 했고 Win32 콘솔은 ANSI 이스케이프 시퀀스를 전혀 지원하지 않는다는 것을 발견했습니다 . Windows의 stdout에 ANSI 지원을 추가하기 위해 여기언급 된 솔루션 을 래핑하기 위해 내 함수에 if 문을 추가 할 수 있습니다 .
Blaker

0

스크립팅 언어를 사용하는 경우 "tput cup"명령을 사용하여이 작업을 수행 할 수 있습니다. PS 이것은 내가 아는 한 Linux / Unix의 문제입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.