Ich habe nichts Ähnliches gesehen und alle benutzerdefinierten Funktionen hier scheinen sich auf das Rendern allein zu konzentrieren. Meine sehr einfache POSIX-kompatible Lösung unten mit schrittweisen Erklärungen, da diese Frage nicht trivial ist.
TL; DR
Das Rendern des Fortschrittsbalkens ist sehr einfach. Die Schätzung, wie viel davon gerendert werden soll, ist eine andere Sache. So rendern (animieren) Sie den Fortschrittsbalken - Sie können dieses Beispiel kopieren und in eine Datei einfügen und ausführen:
#!/bin/sh
BAR='####################' # this is full bar, e.g. 20 chars
for i in {1..20}; do
echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
sleep .1 # wait 100ms between "frames"
done
{1..20}
- Werte von 1 bis 20
echo -n
- am Ende ohne neue Zeile drucken
echo -e
- beim Drucken Sonderzeichen interpretieren
"\r"
- Wagenrücklauf, ein Sonderzeichen, um zum Zeilenanfang zurückzukehren
Sie können dafür sorgen, dass jeder Inhalt mit jeder Geschwindigkeit gerendert wird, sodass diese Methode sehr universell ist, z. B. häufig zur Visualisierung von "Hacking" in albernen Filmen verwendet wird, kein Scherz.
Vollständige Antwort
Das Problem besteht darin, wie der $i
Wert bestimmt wird, dh wie viel des Fortschrittsbalkens angezeigt werden soll. Im obigen Beispiel habe ich es nur in einer for
Schleife inkrementieren lassen , um das Prinzip zu veranschaulichen, aber eine reale Anwendung würde eine Endlosschleife verwenden und die $i
Variable bei jeder Iteration berechnen . Für diese Berechnung werden die folgenden Zutaten benötigt:
- wie viel Arbeit gibt es zu tun
- Wie viel Arbeit wurde bisher geleistet?
Falls cp
es die Größe einer Quelldatei und die Größe der Zieldatei benötigt:
#!/bin/sh
$src=/path/to/source/file
$tgt=/path/to/target/file
cp "$src" "$tgt" & # the & forks the `cp` process so the rest
# of the code runs without waiting (async)
BAR='####################'
src_size=$(stat -c%s "$src") # how much there is to do
while true; do
tgt_size=$(stat -c%s "$tgt") # how much has been done so far
i=$(( $tgt_size * 20 / $src_size ))
echo -ne "\r${BAR:0:$i}"
if [ $tgt_size == $src_size ]; then
echo "" # add a new line at the end
break; # break the loop
fi
sleep .1
done
stat
- Überprüfen Sie die Dateistatistiken
-c
- formatierten Wert zurückgeben
%s
- Gesamtgröße
Bei Vorgängen wie dem Entpacken von Dateien ist die Berechnung der Quellgröße etwas schwieriger, aber immer noch so einfach wie das Abrufen der Größe einer unkomprimierten Datei:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l
- Informationen zum Zip-Archiv anzeigen
tail -n1
- mit 1 Zeile von unten arbeiten
tr -s ' '
- mehrere Leerzeichen in eines übersetzen (zusammendrücken)
cut -d' ' -f3
- 3. durch Leerzeichen getrennte Spalte ausschneiden
Hier ist jedoch das Fleisch des Problems. Diese Lösung ist immer weniger allgemein. Alle Berechnungen des tatsächlichen Fortschritts sind eng an die Domäne gebunden, die Sie visualisieren möchten. Ist es sich um eine einzelne Dateioperation, einen Timer-Countdown, eine steigende Anzahl von Dateien in einem Verzeichnis, eine Operation für mehrere Dateien usw. handelt? kann nicht wiederverwendet werden. Der einzige wiederverwendbare Teil ist das Rendern des Fortschrittsbalkens. Um es wiederzuverwenden, müssen Sie es abstrahieren und in einer Datei speichern (z. B. /usr/lib/progress_bar.sh
) und dann Funktionen definieren, die domänenspezifische Eingabewerte berechnen. So könnte ein verallgemeinerter Code aussehen (ich habe auch die $BAR
Dynamik erzeugt, weil die Leute danach gefragt haben, der Rest sollte jetzt klar sein):
#!/bin/sh
BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)
work_todo=$(get_work_todo) # how much there is to do
while true; do
work_done=$(get_work_done) # how much has been done so far
i=$(( $work_done * $BAR_length / $work_todo ))
echo -ne "\r${BAR:0:$i}"
if [ $work_done == $work_todo ]; then
echo ""
break;
fi
sleep .1
done
printf
- eine integrierte Funktion zum Drucken von Inhalten in einem bestimmten Format
printf '%50s'
- nichts drucken, mit 50 Leerzeichen auffüllen
tr ' ' '#'
- Übersetzen Sie jedes Leerzeichen in ein Hash-Zeichen
Und so würden Sie es verwenden:
#!/bin/sh
src=/path/to/source/file
tgt=/path/to/target/file
function get_work_todo() {
echo $(stat -c%s "$src")
}
function get_work_done() {
[ -e "$tgt" ] && # if target file exists
echo $(stat -c%s "$tgt") || # echo its size, else
echo 0 # echo zero
}
cp "$src" "$tgt" & # copy in the background
source /usr/lib/progress_bar.sh # execute the progress bar
Offensichtlich kann es in eine Funktion eingewickelt, umgeschrieben werden, um mit Pipeline-Streams zu arbeiten, umgeschrieben in eine andere Sprache, was auch immer Ihr Gift ist.