Crea una barra di avanzamento in bash


13

Come posso creare una barra di avanzamento con bash?

Questa è la mia sceneggiatura:

 #!/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

Voglio sostituire echo 'loading 50%'qualsiasi cosa per creare una barra di avanzamento.


1
Una barra di avanzamento nel terminale o una barra di avanzamento in una finestra della GUI separata?
Byte Commander

2
Nel terminal
Black Hawk,

Il etapotrebbe fare quello che vuoi.
aioobe,

Risposte:


14

whiptail viene preinstallato su Ubuntu e su molte altre distribuzioni e mostrerà elementi di progresso a schermo intero (ma ancora basati su terminali).

dialogè un superset di whiptail, quindi questo esempio funzionerà ugualmente bene con entrambi. Fornisce elementi dell'interfaccia utente più avanzati, quindi può tornare utile se stai cercando l'interazione dell'utente come selettori di file e moduli, ma ha lo svantaggio di non essere preinstallato su molti sistemi.

whiptail

dialogo

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

Si noti che l'output dello script viene interpretato come percentuale, quindi potrebbe essere necessario modificare l'output di conseguenza.

Whiptail e Dialog consentono anche di modificare il testo in fase di esecuzione tramite una sintassi piuttosto criptica:

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 l'avanzamento di un file o flusso che viene reindirizzato attraverso di esso. Non può tuttavia essere (facilmente?) Utilizzato per mostrare l'avanzamento di un'operazione personalizzata come un loop. È progettato specificamente per i flussi.

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

Alcuni esempi del mondo reale in cui pvè utile:

# 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

Non conosco alcun comando che fornisca barre di avanzamento di una riga nello stile di pvo wget, ma ci sono molti semplici script Bash / Perl / sed che aggiungeranno tale funzionalità, come altri hanno condiviso qui.


Per mostrare il processo di un ciclo con pvte puoi farlo cercare l'output del ciclo o creare un output falso, ad esempio un echoin ogni iterazione, reindirizzarlo pve dargli il conteggio dell'iterazione -s. Se non è desiderato, ricordati di reindirizzare lo stdout del loop a /dev/null. Ecco un esempio che mostra questo approccio .
dessert

6

È possibile utilizzare zenityper creare semplici finestre di dialogo GTK. Una delle opzioni disponibili è una finestra di dialogo della barra di avanzamento.

Si crea una tale finestra usando zenity --progress. Per renderlo utile, è necessario specificare ulteriori informazioni aggiungendo alcune delle opzioni seguenti (estratto da 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

Esistono due modalità:

  • pulsante : la barra di avanzamento è pulsante, indica solo che qualcosa è in esecuzione, ma non dice nulla sullo stato di avanzamento. Puoi farlo impostando l' --pulsatingopzione.

  • manuale : devi aggiornare l'attuale percentuale di avanzamento allo zenitystandard input del comando per aggiornare la barra di avanzamento.
    Un esempio potrebbe essere il seguente. Si noti che i comandi precedenti sono raggruppati in una subshell in modo che tutto l'output venga reindirizzato alla zenityfinestra di dialogo e non solo a quello dell'ultimo comando:

    (echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress

Nel caso in cui questa sarebbe anche un'opzione.
Byte Commander

1
Scusa mia cara, questa è la finestra della barra di avanzamento della GUI, voglio creare una barra di avanzamento nel terminale, ad esempio voglio vederlo mentre lo script sta controllando ==>[ ###########--------------] 52%
Black Hawk

1
Si, capisco. È solo che avevo già scritto metà della mia risposta quando l'hai detto, quindi ho deciso di pubblicarlo comunque nel caso in cui qualcun altro potesse averne bisogno in futuro. Spero non ti dispiaccia, dato che ci sono anche parecchie soluzioni basate su terminali.
Byte comandante

5

Questo codice lo farà e non richiede nulla (tranne ovviamente bash). Stampa i #segni, come hai chiesto nel tuo commento:

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

Tuttavia, se hai molto da controllare, questo riempirà lo schermo di #segni. Per risolvere il problema, prova questo codice:

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

Cambia il 5 nella prima riga ( lenProgressBar=5) nella lunghezza desiderata per la barra di avanzamento. Ci vorrà più tempo per stampare un #segno con barre di avanzamento di lunghezza inferiore rispetto a quelle di lunghezza maggiore, ma non lasciare che la lunghezza della barra di avanzamento superi le dimensioni dello schermo; non funzionerà bene se lo fai. (Non ti consentirà di utilizzare una barra di avanzamento superiore al numero di elementi che stai controllando o inferiore a 1)


1
È possibile utilizzare tput colsper rilevare la larghezza della finestra del terminale e ridimensionare la barra di avanzamento di conseguenza.
Mikkel,

1

Ecco un altro approccio che utilizza i codici di 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.