Erstellen Sie einen Fortschrittsbalken in Bash

13

Wie kann ich mit bash einen Fortschrittsbalken erstellen?

Das ist mein Drehbuch:

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

Ich möchte durch echo 'loading 50%'irgendetwas ersetzen , um einen Fortschrittsbalken zu erstellen.

Schwarzer Falke
quelle
1
Eine Fortschrittsanzeige im Terminal oder eine Fortschrittsanzeige in einem separaten GUI-Fenster?
Byte Commander
2
Im Terminal
Black Hawk
Der macht etavielleicht was du willst.
Aioobe

Antworten:

14

whiptail ist auf Ubuntu und vielen anderen Distributionen vorinstalliert und zeigt Fortschrittselemente im Vollbildmodus (aber immer noch terminalbasiert) an.

dialogist eine Obermenge von whiptail, daher funktioniert dieses Beispiel mit beiden gleich gut. Es bietet fortgeschrittenere UI-Elemente, so dass es nützlich sein kann, wenn Sie nach Benutzerinteraktionen wie Dateiauswahl und Formularen suchen, aber es hat den Nachteil, dass es auf vielen Systemen nicht vorinstalliert ist.

Peitschenschwanz

Dialog

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

Beachten Sie, dass die Skriptausgabe als Prozentsatz interpretiert wird, sodass Sie möglicherweise Ihre Ausgabe entsprechend anpassen müssen.

Mit Whiptail und Dialog können Sie den Text auch zur Laufzeit über eine eher kryptische Syntax ändern:

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

pvZeigt den Fortschritt einer Datei oder eines Streams an, der durch die Datei geleitet wird. Es kann jedoch nicht (einfach?) Verwendet werden, um den Fortschritt einer benutzerdefinierten Operation wie einer Schleife anzuzeigen. Es wurde speziell für Streams entwickelt.

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

Einige Beispiele aus der pvPraxis, die sich als nützlich erweisen:

# 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

Ich kenne keine Befehle, die einzeilige Fortschrittsbalken im Stil von pvoder enthalten wget, aber es gibt viele einfache Bash / Perl / sed-Skripte, die diese Funktionalität hinzufügen, wie andere hier geteilt haben.

Mikkel
quelle
Um zu zeigen , den Prozess einer Schleife mit pvIhnen entweder kann es für die Schleife der Ausgabe aussehen lassen oder eine gefälschte Ausgabe erstellen, zB eine echoin jeder Iteration, eine Pipe an pvund gibt ihm die Iterationszahlcode mit -s. Wenn dies nicht erwünscht ist, denken Sie daran, die Standardausgabe der Schleife an umzuleiten /dev/null. Hier ist ein Beispiel, das diesen Ansatz zeigt .
Dessert
6

Mit können Sie zenityeinfache GTK-Dialogfenster erstellen. Eine der verfügbaren Optionen ist ein Fortschrittsbalkendialog.

Sie erstellen ein solches Fenster mit zenity --progress. Um es nützlich zu machen, sollten Sie weitere Informationen angeben, indem Sie einige der folgenden Optionen hinzufügen (Auszug aus 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

Es gibt zwei Modi:

  • pulsierend : Der Fortschrittsbalken pulsiert, zeigt nur an, dass etwas läuft, sagt aber nichts über den Fortschritt aus. Dazu setzen Sie die --pulsatingOption.

  • manuell : Sie müssen den aktuellen Fortschrittsprozentsatz an die zenityStandardeingabe des Befehls weiterleiten, um die Fortschrittsanzeige zu aktualisieren.
    Ein Beispiel dafür könnte wie folgt aussehen. Beachten Sie, dass die vorherigen Befehle in einer Subshell zusammengefasst sind, sodass die gesamte Ausgabe in den zenityDialog und nicht nur in den des letzten Befehls umgeleitet wird :

    (echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress
Byte Commander
quelle
Nur für den Fall, dass dies auch eine Option wäre.
Byte Commander
1
Tut mir leid, meine Liebe, dies sind GUI-Fortschrittsbalkenfenster. Ich möchte einen Fortschrittsbalken im Terminal erstellen. Ich möchte ihn zum Beispiel sehen, während das Skript prüft. ==>[ ###########--------------] 52%
Black Hawk
1
Ja ich verstehe. Es ist nur so, dass ich bereits die Hälfte meiner Antwort geschrieben hatte, als Sie das sagten, also habe ich mich entschlossen, es trotzdem zu posten, falls jemand anderes es in Zukunft brauchen sollte. Ich hoffe, es macht Ihnen nichts aus, da es auch einige Terminal-basierte Lösungen gibt.
Byte Commander
5

Dieser Code wird es tun und erfordert nichts (außer Bash natürlich). Es werden #Zeichen gedruckt , wie Sie in Ihrem Kommentar gefragt haben:

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

Wenn Sie jedoch viel zu überprüfen haben, wird dies nur Ihren Bildschirm mit #Zeichen füllen . Versuchen Sie diesen Code, um dieses Problem zu beheben:

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

Ändern Sie die 5 in der ersten Zeile ( lenProgressBar=5) auf die Länge, die Ihre Fortschrittsanzeige haben soll. Es dauert länger, ein #Schild mit kürzeren Fortschrittsbalken als mit längeren zu drucken. Achten Sie jedoch darauf, dass die Länge des Fortschrittsbalkens die Bildschirmgröße nicht überschreitet. es wird nicht gut funktionieren, wenn du es tust. (Sie können keinen Fortschrittsbalken verwenden, der höher als die Anzahl der überprüften Elemente oder niedriger als 1 ist.)

insert_name_here
quelle
1
Sie tput colskönnen die Breite des Terminalfensters ermitteln und die Fortschrittsanzeige entsprechend skalieren.
Mikkel
1

Hier ist ein anderer Ansatz, der ansi-Escape-Codes verwendet:

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