Criar uma barra de progresso no bash

13

Como posso criar uma barra de progresso com o bash?

Este é o meu script:

 #!/bin/bash
 pass='number1 number12 number13 number14 number15 number16'
 chk='number14'
 for i in $pass ; do
 if [ "$i" == "$chk" ]; then
 echo ' Found ^_^'
 else
 echo 'loading 50%'
 fi
 done

Quero substituir echo 'loading 50%'por qualquer coisa para criar uma barra de progresso.

Falcão
fonte
1
Uma barra de progresso no terminal ou uma barra de progresso em uma janela GUI separada?
Byte Commander
2
No terminal
Black Hawk
O etapode fazer o que quiser.
aioobe

Respostas:

14

whiptail vem pré-instalado no Ubuntu e em muitas outras distribuições e mostra elementos de progresso em tela cheia (mas ainda com base em terminal).

dialogé um superconjunto de whiptail, portanto, este exemplo funcionará igualmente bem com ambos. Ele fornece elementos de interface do usuário mais avançados, por isso pode ser útil se você estiver procurando por interação do usuário, como selecionadores de arquivos e formulários, mas tem a desvantagem de não vir pré-instalado em muitos sistemas.

chicote

diálogo

for i in $(seq 1 100)
do
    sleep 0.1 
    echo $i
done | whiptail --title 'Test script' --gauge 'Running...' 6 60 0

Observe que a saída do script é interpretada como uma porcentagem; portanto, você pode precisar ajustar sua saída de acordo.

Whiptail e Dialog também permitem modificar o texto em tempo de execução por meio de uma sintaxe bastante enigmática:

phases=( 
    'Locating Jebediah Kerman...'
    'Motivating Kerbals...'
    'Treating Kessler Syndrome...'
    'Recruiting Kerbals...'
)   

for i in $(seq 1 100); do  
    sleep 0.1

    if [ $i -eq 100 ]; then
        echo -e "XXX\n100\nDone!\nXXX"
    elif [ $(($i % 25)) -eq 0 ]; then
        let "phase = $i / 25"
        echo -e "XXX\n$i\n${phases[phase]}\nXXX"
    else
        echo $i
    fi 
done | whiptail --title 'Kerbal Space Program' --gauge "${phases[0]}" 6 60 0

pvmostra o progresso de um arquivo ou fluxo sendo canalizado por ele. No entanto, não pode ser (facilmente?) Usado para mostrar o progresso de uma operação personalizada, como um loop. Ele foi projetado especificamente para fluxos.

$ head -c 1G < /dev/urandom | pv -s 1G > /dev/null
 277MB 0:00:16 [17.4MB/s] [========>                           ] 27% ETA 0:00:43

Alguns exemplos do mundo real são pvúteis:

# progress while importing a DB dump
pv mybigfile.sql | mysql -uroot -p dbname

# importing straight from a remote server
ssh user@server 'cat mybigfile.sql.gz' | pv | gzip -cd | mysql -uroot -p dbname

# taking a snapshot of a btrfs partition
btrfs send /snapshots/$date | pv | btrfs receive /mnt/backup/root

Não conheço nenhum comando que ofereça barras de progresso de uma linha no estilo de pvou wget, mas existem muitos scripts Bash / Perl / sed simples que adicionarão essa funcionalidade, como outros compartilhados aqui.

Mikkel
fonte
Para mostrar o processo de um loop, pvvocê pode fazer com que ele procure a saída do loop ou criar alguma saída falsa, por exemplo, uma echoem cada iteração, canalize-a pve faça a contagem da iteração -s. Se não for desejado, lembre-se de redirecionar o stdout do loop para /dev/null. Aqui está um exemplo mostrando essa abordagem .
dessert
6

Você pode usar zenitypara criar simples janelas de diálogo GTK. Uma das opções disponíveis é uma caixa de diálogo da barra de progresso.

Você cria essa janela usando zenity --progress. Para torná-lo útil, você deve especificar mais informações adicionando algumas das opções abaixo (trecho de man zenity):

   Progress options
   --text=STRING
          Set the dialog text
   --percentage=INT
          Set initial percentage
   --auto-close
          Close dialog when 100% has been reached
   --auto-kill
          Kill parent process if cancel button is pressed
   --pulsate
          Pulsate progress bar
   --no-cancel
          Hides the cancel button

Existem dois modos:

  • pulsante : a barra de progresso está pulsando, apenas indica que algo está sendo executado, mas não diz nada sobre o progresso. Você faz isso definindo a --pulsatingopção

  • manual : você deve canalizar a porcentagem de progresso atual para a zenityentrada padrão do comando para atualizar a barra de progresso.
    Um exemplo para isso pode se parecer com o abaixo. Observe que os comandos anteriores são agrupados em um subshell para que toda a saída seja redirecionada para a zenitycaixa de diálogo e não apenas para o último comando:

    (echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress
Byte Commander
fonte
Apenas no caso de isso também seria uma opção.
Byte Commander
1
Desculpe minha querida, este é janelas barra de progresso GUI, eu quero criar uma barra de progresso no terminal, por exemplo, eu quero vê-lo enquanto o script está verificando ==>[ ###########--------------] 52%
Black Hawk
1
Sim, eu entendo. Só que eu já tinha escrito metade da minha resposta quando você disse isso, então decidi publicá-la de qualquer maneira, caso outra pessoa pudesse precisar no futuro. Espero que você não se importe, pois também existem algumas soluções baseadas em terminais.
Byte Commander
5

Esse código fará isso e não requer nada (exceto bash, é claro). Imprime #sinais, como você pediu no seu comentário:

pass='number1 number12 number13 number14 number15 number16'
chk='number14'
passarr=($pass)
lenProgressBar=${#passarr[@]}

echo -n '['
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -n '-'
    ((i++))
done

echo -n ']'
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -e -n '\b'
    ((i++))
done

echo -e -n '\b'
for i in $pass ; do
    if [ "$i" = "$chk" ]; then
        echo -e '#\nFound ^_^'
        break
    else
        echo -n '#'
    fi
done

No entanto, se você tiver muito o que verificar, isso preencherá sua tela com #sinais. Para corrigir esse problema, tente este código:

lenProgressBar=5
pass='number1 number12 number13 number14 number15 number16'
chk='number14'
passarr=($pass)
lenPass=${#passarr[@]}

if [ $lenProgressBar -gt $lenPass ]; then
    lenProgressBar=lenPass
elif [ $lenProgressBar -lt 1 ]; then
    lenProgressBar=1
fi

let "chksForEqualsPrint = $lenPass / $lenProgressBar"
echo -n '['
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -n '-'
    ((i++))
done

echo -n ']'
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -e -n '\b'
    ((i++))
done

echo -e -n '\b'
n=1

for i in $pass ; do
    if [ "$i" = "$chk" ]; then
        echo -e '\nFound ^_^'
        break
    else
        if [ $n -eq $chksForEqualsPrint ]; then
            echo -n '#'
            n=1
        else
            ((n++))
        fi
    fi
done

Altere o 5 na primeira linha ( lenProgressBar=5) para o comprimento que você deseja que sua barra de progresso. Levará mais tempo para imprimir um #sinal com barras de progresso de comprimento menor do que com barras de comprimento maior, mas não deixe que o comprimento da barra de progresso exceda o tamanho da tela; não vai funcionar bem se você fizer. (Não permitirá que você use uma barra de progresso maior que o número de itens que você está verificando ou menor que 1)

insert_name_here
fonte
1
Você pode usar tput colspara detectar a largura da janela do terminal e dimensionar a barra de progresso de acordo.
Mikkel
1

Aqui está outra abordagem usando códigos de escape ansi:

#!/bin/bash

pass='number1 number2 number 3 number4 number12 number13 number14 number15 number16'
chk='number15'
result="Not Found!"

echo
echo -n "Working... "
echo -ne "\033[1;32m\033[7m\033[?25l"

for i in $pass ; do
   sleep .4s
   if [ "$i" == "$chk" ]; then
      result="  Found ^_^"
      break
   else
      echo -n " "
   fi
done

echo -ne "\r\033[0m\033[K\033[?25h"
echo $result
echo
bashBedlam
fonte