Assim, ao wget
acessar uma página da web, ela mostra uma barra de status que indica quanto o(s) arquivo(s) foi baixado(s). Se parece com isso:
25%[=============>______________________________________] 25,000 100.0K/s
(sublinhados são espaços; simplesmente não consegui descobrir como colocar mais de um espaço consecutivo ali)
Porém, em vez de escrever outra linha no stdout e adicionar outra barra de progresso, ele a atualiza, assim:
50%[===========================>________________________] 50,000 100.0K/s
E wget
também não é o único exemplo disso. Por exemplo, quando você canaliza algo less
e sai, seu prompt original ainda está lá, junto com o resultado de quaisquer comandos que você executou anteriormente. É como se você nunca tivesse partido.
Então, minhas perguntas são: como se chama isso, como faço para implementá-lo, funciona apenas para uma única linha por vez e posso usar isso em C?
Responder1
Primeiro de tudo, sua pergunta não tem nada a ver com o bash, mas com o terminal. O terminal está respondendo por exibir o texto dos programas e o próprio bash não tem controle sobre os programas depois de iniciados.
Os terminais oferecem sequências de controle para controlar cor, fonte, posição do cursor e muito mais. Para obter uma lista de sequências terminais padronizadas, dê uma olhada em http://www.termsys.demon.co.uk/vtansi.htmVocê pode por exemplo
- posicione o cursor no início da linha
- exclua a linha depois
- escreva uma nova linha
para criar uma barra de progresso.
Sequências de escape de terminal mais avançadas são tipicamente dependentes de terminal, por exemplo, funcionam apenas com Eterm ou xterm.maldições- é uma biblioteca de programação que permite criar programas interativos com o terminal para que você não precise usar sequências de escape.
Como substituir uma linha existente com sequências de terminais
echo long text
sleep 1
printf "\033[1A" # move cursor one line up
printf "\033[K" # delete till end of line
echo foo
Como substituir uma linha existente sem sequência de terminal
Uma solução simples é não escrever uma nova linha no final, mas escrever um retorno de carro, que basicamente redefine o cursor para o início da linha, por exemplo:
echo -n first
sleep 1
echo -ne "\rsecond"
echo
O \r
retorno de carro ou colocará o cursor no início da linha e permitirá sobrescrever o conteúdo da linha.
Alternar entre buffers como less
ouvi
O comportamento less
também se deve a um recurso mais avançado do terminal, a tela alternativa:
No modo VT102, existem sequências de escape para ativar e desativar um buffer de tela alternativo, que tem o mesmo tamanho da área de exibição da janela. Quando ativado, a tela atual é salva e substituída pela tela alternativa. O salvamento das linhas roladas na parte superior da janela fica desativado até que a tela normal seja restaurada. A entrada term-cap(5) para xterm permite que o editor visual vi(1) mude para a tela alternativa para edição e restaure a tela ao sair. Uma entrada de menu pop-up simplifica a alternância entre as telas normal e alternativa para recortar e colar.
http://rosettacode.org/wiki/Terminal_control/Preserve_screenlista alguns exemplos de como fazer você mesmo, seja viatputou através de algumas sequências de escape.
Responder2
Em vez de usar echo
which anexa automaticamente uma nova linha à string, use printf "%s\r" whatever
-- o retorno de carro envia o cursor para o início da linha atual. exemplo:
seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""
Responder3
Recomendo também a quem encontrou este tópico que dê uma olhadaBash Prompt HOWTO - Movimento do Cursor.
Exemplos:
- Position the Cursor:
\033[<L>;<C>H
Or
\033[<L>;<C>f
puts the cursor at line L and column C.
- Move the cursor up N lines:
\033[<N>A
- Move the cursor down N lines:
\033[<N>B
- Move the cursor forward N columns:
\033[<N>C
- Move the cursor backward N columns:
\033[<N>D
- Clear the screen, move to (0,0):
\033[2J
- Erase to end of line:
\033[K
- Save cursor position:
\033[s
- Restore cursor position:
\033[u
Alguns exemplos em C:
void saveCursorPosition() {
printf("\033[s");
}
void restoreCursorPosition() {
printf("\033[u");
}
void lineUP(short int times) {
printf("\033[%iA", times);
}
void lineDown(short int times) {
printf("\033[%iB", times);
}
Amostra do programa:
#include <stdio.h>
#include <unistd.h>
void saveCursorPosition() {
printf("\033[s");
}
void restoreCursorPosition() {
printf("\033[u");
}
void lineUP(short int times) {
printf("\033[%iA", times);
}
void moveCursorBackwards(short int times) {
printf("\033[%iD", times);
}
void printMainText() {
printf("\n ╔═══════════════════════════════╗");
printf("\n ║ ║");
printf("\n ║ Progress Bar ║");
printf("\n ║ ║");
printf("\n ║ [] ║");
printf("\n ║ ║");
printf("\n ║ Press Ctrl+C to close ║");
printf("\n ║ ║");
printf("\n ╚═══════════════════════════════╝\n");
}
int main(int argc, char **argv) {
printMainText();
for (int progress=0; progress <= 10; progress++) {
saveCursorPosition();
lineUP(5);
printf("\r ║ [");
fflush(stdout);
for (int i=0; i<progress; i++) {
printf("=>]");
fflush(stdout);
moveCursorBackwards(2);
}
moveCursorBackwards(progress + 5);
printf("%i%%", progress * 10);
fflush(stdout);
restoreCursorPosition();
sleep(1);
}
return 0;
}