Como substituir uma linha existente com sequências de terminais

Como substituir uma linha existente com sequências de terminais

Assim, ao wgetacessar 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 wgettambém não é o único exemplo disso. Por exemplo, quando você canaliza algo lesse 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 \rretorno de carro ou colocará o cursor no início da linha e permitirá sobrescrever o conteúdo da linha.

Alternar entre buffers como lessouvi

O comportamento lesstambé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 echowhich 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;

}

informação relacionada