Utwórz pasek postępu w bash

14

Jak mogę utworzyć pasek postępu za pomocą bash?

To jest mój skrypt:

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

Chcę zastąpić echo 'loading 50%'czymkolwiek, aby utworzyć pasek postępu.

Czarny Jastrząb
źródło
1
Pasek postępu w terminalu czy pasek postępu w osobnym oknie GUI?
Bajt Dowódca
2
W terminalu
Black Hawk
etaMoże robić, co chcesz.
aioobe

Odpowiedzi:

14

whiptail jest fabrycznie zainstalowany na Ubuntu i wielu innych dystrybucjach i wyświetla elementy postępu na pełnym ekranie (ale nadal oparte na terminalu).

dialogjest nadzbiorem whiptail, więc ten przykład będzie działał równie dobrze z oboma. Zapewnia bardziej zaawansowane elementy interfejsu użytkownika, więc może się przydać, jeśli szukasz interakcji użytkownika, takiej jak selektory plików i formularze, ale ma tę wadę, że nie jest instalowany fabrycznie w wielu systemach.

whiptail

dialog

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

Zauważ, że wynik skryptu jest interpretowany jako procent, więc może być konieczne odpowiednie dostosowanie wyniku.

Whiptail i Dialog pozwalają również modyfikować tekst w czasie wykonywania za pomocą raczej tajemniczej składni:

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

pvpokazuje postęp pliku lub strumienia przesyłanego przez niego. Nie można go jednak (łatwo?) Wykorzystać do pokazania postępu niestandardowej operacji, takiej jak pętla. Jest zaprojektowany specjalnie dla strumieni.

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

pvPrzydaje się kilka przykładów ze świata rzeczywistego :

# 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

Nie znam żadnych poleceń, które dają paski postępu w jednym wierszu w stylu pvlub wget, ale istnieje wiele prostych skryptów Bash / Perl / sed, które dodadzą tę funkcjonalność, tak jak inni tutaj udostępnili.

Mikkel
źródło
Aby pokazać proces pętli za pomocą pv, możesz sprawić, że będzie szukać wyjścia pętli lub stworzyć fałszywe wyjście, np. echoW każdej iteracji, potokuj do niego pvi podaj mu liczbę iteracji -s. Jeśli nie jest to pożądane, pamiętaj o przekierowaniu stdout pętli na /dev/null. Oto przykład pokazujący to podejście .
deser
6

Możesz użyć zenitydo stworzenia prostych okien dialogowych GTK. Jedną z dostępnych opcji jest okno dialogowe paska postępu.

Takie okno tworzysz za pomocą zenity --progress. Aby było to przydatne, należy podać więcej informacji, dodając niektóre z poniższych opcji (fragment z 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

Istnieją dwa tryby:

  • pulsuje : pasek postępu pulsuje, po prostu wskazuje, że coś działa, ale nie mówi nic o postępie. Robisz to, ustawiając --pulsatingopcję.

  • manual : Musisz przesłać bieżący procent postępu do zenitystandardowego wejścia polecenia, aby zaktualizować pasek postępu.
    Przykład tego może wyglądać tak poniżej. Zauważ, że poprzednie polecenia są pogrupowane w podpowłokę, dzięki czemu wszystkie dane wyjściowe są przekierowywane do zenityokna dialogowego, a nie tylko ostatniego polecenia:

    (echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress
Bajt Dowódca
źródło
Na wszelki wypadek byłaby to również opcja.
Bajt Dowódca
1
Przepraszam moja droga, to są okna paska postępu GUI, chcę utworzyć pasek postępu w terminalu, na przykład chcę go zobaczyć podczas sprawdzania skryptu ==>[ ###########--------------] 52%
Black Hawk
1
Tak, rozumiem. Po prostu napisałem już połowę mojej odpowiedzi, kiedy to powiedziałeś, więc postanowiłem ją opublikować na wypadek, gdyby ktoś inny mógł jej potrzebować w przyszłości. Mam nadzieję, że nie masz nic przeciwko, ponieważ istnieje również sporo rozwiązań opartych na terminalach.
Bajt Dowódca
5

Ten kod to zrobi i nie wymaga niczego (oczywiście oprócz bash). Drukuje #znaki, tak jak prosiłeś w swoim komentarzu:

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

Jeśli jednak masz dużo do sprawdzenia, spowoduje to wypełnienie ekranu #znakami. Aby rozwiązać ten problem, wypróbuj ten kod:

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

Zmień 5 w pierwszym wierszu ( lenProgressBar=5) na długość, która ma być na pasku postępu. Wydrukowanie #znaku z paskami postępu o mniejszej długości potrwa dłużej niż z pasami o większej długości, ale nie pozwól, aby długość paska postępu przekroczyła rozmiar ekranu; to nie zadziała dobrze, jeśli to zrobisz. (Nie pozwoli ci to użyć paska postępu wyższego niż liczba sprawdzanych elementów lub niższego niż 1)

wstaw nazwę_tutaj
źródło
1
Możesz użyć tput colsdo wykrycia szerokości okna terminala i odpowiedniego skalowania paska postępu.
Mikkel
1

Oto inne podejście przy użyciu kodów specjalnych 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
źródło