[Bash] Construindo indicador de progresso

Leandro Nunes
Meninunes
Published in
5 min readMar 26, 2018

Atualmente tenho trabalhado bastante com shell e automação, então pretendo começar a escrever um pouco mais sobre o mundo no terminal e tentar trazer algumas coisas interessantes em Bash script que um Desenvolvedor Front-end acaba se perguntando como fazer para tentar trazer uma melhora na usabilidade dos scripts.

Bash is a Unix shell and command language

Hoje iremos ver como fazer um indicador de progresso, totalmente em Bash e sem nenhuma biblioteca externa, onde é possível utilizar em algum processo de instalação, ou em um processo demorado ou simplesmente para mostrar visualmente quantos comandos faltam a ser executado em determinada tarefa de forma que o usuário não fique esperando sem nenhum feedback.

No Bash uma das formas de exibirmos algo para o usuário é através do comando echo

#!/bin/bash
echo "Hello World!"
// Hello World!

E se fizermos dois echos seguidos, automaticamente é adicionado uma quebra de linha:

#!/bin/bash
echo "Hello"
echo "World!"
// Hello
// World!

Esse é o primeiro problema ao se pensar em um indicador de progresso, pois indicador de progresso não pode ficar pulando de linha:

log progressivo

O que queremos é algo próximo disso:

log indicador de progresso

Começando a programar

Para acompanhar os códigos a seguir, é só criar um progress.sh localmente e dar permissão de execução a ele com o comando chmod +x progress.sh.
Este comando só é necessário uma única vez, após isso, para executá-lo é só digitar ./progress.sh na linha de comando do terminal.

For..loop

Primeiro vamos fazer um loop com o comando for e colocar o nosso echo:

Bash online Compiler: http://rextester.com/FWY74472

Calculando a porcentagem

Agora que temos a sintaxe do for, vamos calcular e exibir a porcentagem ao invés do índice do loop.

Operações matemáticas no Bash, são feitos da seguinte forma:

#!/bin/bash
echo "$((2 + 2))"
// 4

Com isso já podemos então fazer nosso for..loop calculando a porcentagem em cada iteração do loop:

Bash online Compiler: http://rextester.com/YXID57940

Exibindo a barra de progresso

Agora vamos adicionar uma barra de progresso dentro dos colchetes com o caracter "#" conforme a variação da porcentagem. Para isso, precisamos saber como exibir uma parte de uma string. No JavaScript poderíamos fazer isso através do método substring por exemplo. No Bash, se eu quiser exibir apenas o meu primeiro nome, ficaria assim:

#!/bin/bash
declare -r name="Leandro Nunes"
echo "${name:0:7}"
// Leandro

Onde,
“:0” é o ponto de início
“:7” é o ponto de fim

Agora que vimos como fazer substring em Bash, podemos evoluir nosso código e adicionar uma barra visual de progresso da seguinte forma:

Bash online Compiler: http://rextester.com/LDFSW76492

Mantendo o log na mesma linha

Conseguimos até o momento fazer um log progressivo, porém o resultado final desejado é um log indicador de progresso e para isso falta conseguirmos manter todos os logs na mesma linha, dessa forma teremos uma atualização em cada loop e apenas o último estado será exibido.

Um detalhe sobre o comando echo é que ele não aceita por padrão caracteres especiais de controle, por exemplo, um simples \n :

#!/bin/bash
echo "Hello\nWorld!"
// Hello\nWorld!

O comando echo possui algumas flags especiais que adicionam "poder" a ele, e para o caso acima funcionar, temos que adicionar uma flag -e.

#!/bin/bash
echo -e "Hello\nWorld!"
// Hello
// World!

Outra flag que existe é -n e além do -e é exatamente essa que nos interessa. Pois ela mantém o ponteiro do curso na mesma linha, ou seja, ela não pula para a próxima linha ao executarmos dois echo seguidos.

#!/bin/bash
echo -n "Hello"
echo -n "World!"
// World!

Então vamos adicionar a flag -n e teremos o seguinte:

Bash online Compiler: http://rextester.com/SKNB80695

Observe que adicionei um sleep 1 na linha 13 para simular algum comando real que estivéssemos executando e obtermos um efeito de animação progressiva

As flags podem ser combinadas, então ao invés de usar apenas a flag -n iremos utilizar -ne desta forma além de manter na mesma linha, conseguiremos utilizar caracteres especial de controle e existe um que volta o ponteiro para o início da linha corrente \\r.

Bash online Compiler: http://rextester.com/VZRBW16090

Assim conseguimos o resultado esperado! 😃

Ops, quer dizer…
Conseguiu notar que não está exatamente igual a imagem que mostrei no inicio do post ? Percebe que temos um cursor aparecendo e quando chega no final exibe 100%% !?!!! 😕

Lapidando e finalização do resultado

Para esconder e exibir o cursor, temos o seguinte comando:

#!/bin/bash
# Esconde o cursor
tput civis -- invisible
# Volta cursor ao normal
tput cnorm -- normal

Para resolver o último caracter que fica estranho, é devido a flag -n que estamos utilizando, como ele mantém o cursos na mesma linha, qdo sai do programa e volta para o shell é inserido um caracter estranho, então basta adicionar um echo vazio após o loop e com isso o ponteiro volta ao estado normal. Então nosso código final fica assim:

http://rextester.com/JAGD20301

Resultado final !!!

log indicador de progresso

Veja outros exemplos de uso um pouco mais elaborados,
neste repositório do Github.

👋 Se curtiu o artigo, clique no “Clap” para aumentar a visibilidade do artigo!
-
Ajude a divulgar compartilhando
- Ajude a melhorar comentando
Qualquer dúvida ou sugestão me chame no Twitter (@lnfnunes)

Little abraços

--

--