Como alterar o conteúdo de uma linha no terminal em vez de escrever uma nova?

24

Portanto, quando wgetobtém uma página da Web, ela mostra uma barra de status que indica quanto os arquivos são baixados. Se parece com isso:

25%[=============>______________________________________] 25,000 100.0K/s (sublinhados são espaços; eu simplesmente não conseguia descobrir como obter mais de um espaço consecutivo)

No entanto, em vez de escrever outra linha no stdout e adicionar outra barra de progresso, ele é atualizado da seguinte maneira:

50%[===========================>________________________] 50,000 100.0K/s

E wgettambém não é o único exemplo disso. Por exemplo, quando você canaliza algo para dentro lesse depois sai, o prompt original ainda está lá, junto com o resultado de qualquer comando executado anteriormente. É como se você nunca fosse embora.

Portanto, minhas perguntas são: como é chamado, como implementá-lo, ele funciona apenas para uma única linha de cada vez e posso usá-lo em C?

fouric
fonte
5
Eu recomendo a leitura do BashFAQ 44 . Você pode achar interessante.
Jw013

Respostas:

32

Antes de tudo, sua pergunta não tem nada a ver com o bash, mas com o terminal. O terminal está respondendo pela exibição do texto dos programas e o bash em si não tem controle sobre os programas após o lançamento.

Os terminais oferecem seqüências de controle para controlar cor, fonte, posição do cursor e muito mais. Para obter uma lista de seqüências de terminais padronizadas, consulte http://www.termsys.demon.co.uk/vtansi.htm Você pode, por exemplo,

  • posicione o cursor no início da linha
  • apague a linha depois
  • escreva uma nova linha

para criar uma barra de progresso.

Sequências de escape de terminal mais avançadas geralmente dependem do terminal, por exemplo, funcionam apenas com Eterm ou xterm. ncurses - é uma biblioteca de programação que cria programas interativos com o terminal para que você não precise usar seqüências de escape.

Como sobrescrever uma linha existente com seqüê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 sobrescrever uma linha existente sem sequência de terminais

Uma solução simples é não escrever uma nova linha no final, mas escrever 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á substituir o conteúdo da linha.

Alterne entre buffers como lessouvi

O comportamento de lesstambém é devido a um recurso de terminal mais avançado, a tela alternativa:

No modo VT102, existem seqüências de escape para ativar e desativar um buffer de tela alternativo, que é do mesmo tamanho da área de exibição da janela. Quando ativada, a tela atual é salva e substituída pela tela alternativa. O salvamento das linhas roladas para cima da janela é desativado até que a tela normal seja restaurada. A entrada do termo cap (5) para o xterm permite que o editor visual vi (1) mude para a tela alternativa para edição e restaure a tela na saída. Uma entrada de menu pop-up facilita a alternância entre as telas normal e alternativa para recortar e colar.

http://rosettacode.org/wiki/Terminal_control/Preserve_screen lista alguns exemplos de como fazer você mesmo, seja por tput ou por algumas seqüências de escape.

Ulrich Dangel
fonte
15

Em vez de usar o echoque 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 ""
Glenn Jackman
fonte
dependendo cursor do seu terminal, pode ser mais agradável para fazerprintf "\r%2d " $num
Glenn Jackman