[Bash] Construindo indicador de progresso
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.
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:
O que queremos é algo próximo disso:
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:
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:
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:
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:
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
.
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:
Resultado final !!!
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