Pergunta
Sempre me perguntei como as pessoas atualizam uma linha anterior em uma linha de comando.um ótimo exemplo disso é ao usar o comando wget no Linux.Ele cria uma espécie de barra de carregamento ASCII semelhante a esta:
[======> ] 37%
e é claro que a barra de carregamento se move e a porcentagem muda, mas isso não cria uma nova linha.Não consigo descobrir como fazer isso.Alguém pode me apontar na direção certa?
Solução
Existem duas maneiras que conheço de fazer isso:
- Use o caractere de escape backspace ('\b') para apagar sua linha
- Use o
curses
pacote, se a linguagem de programação de sua escolha tiver ligações para ele.
E um Google revelou Códigos de escape ANSI, o que parece ser um bom caminho.Para referência, aqui está uma função em C++ para fazer isso:
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.
}
Outras dicas
Uma maneira de fazer isso é atualizar repetidamente a linha de texto com o progresso atual.Por exemplo:
def status(percent):
sys.stdout.write("%3d%%\r" % percent)
sys.stdout.flush()
Observe que eu usei sys.stdout.write
em vez de print
(isto é Python) porque print
imprime automaticamente "
" (nova linha de retorno de carro) no final de cada linha.Eu só quero o retorno de carro que retorna o cursor para o início da linha.Também o flush()
é necessário porque, por padrão, sys.stdout
apenas libera sua saída após uma nova linha (ou depois que seu buffer fica cheio).
O segredo é imprimir apenas em vez de ou no e da linha.
é chamado de retorno de carro e move o cursor no início da linha
n é chamado de alimentação de linha e move o cursor na próxima linha no console.Se você usar apenas você sobrescreve a linha escrita anteriormente.Então primeiro escreva uma linha como a seguinte:
[ ]
em seguida, adicione um sinal para cada tick
\r[= ]
\r[== ]
...
\r[==========]
e assim por diante.Você pode usar 10 caracteres, cada um representando 10%.Além disso, se você quiser exibir uma mensagem quando terminar, não se esqueça de adicionar caracteres brancos suficientes para substituir os sinais de igual escritos anteriormente, assim:
\r[done ]
abaixo está minha resposta, use a API do WindowsConsolas (Windows), codificação de 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;
}
O PowerShell tem um cmdlet Write-Progress que cria uma barra de progresso no console que você pode atualizar e modificar à medida que seu script é executado.
Aqui está a resposta para sua pergunta...(Pitão)
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")
Como acompanhamento A resposta de Greg, aqui está uma versão estendida de sua função que permite exibir mensagens multilinhas;basta passar uma lista ou tupla das strings que você deseja exibir/atualizar.
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()
Observação:Eu testei isso apenas usando um terminal Linux, então sua milhagem pode variar em sistemas baseados em Windows.
Se você estiver usando uma linguagem de script, poderá usar o comando "tput cup" para fazer isso ...P.S.Até onde eu sei, isso é coisa de Linux/Unix...