Wie ein Fortschrittsbalken zu einem Shell-Skript hinzufügen?
Frage
Wenn Scripting in bash oder einer anderen Schale in * NIX, während ein Befehl ausgeführt wird, der mehr als ein paar Sekunden dauert, wird ein Fortschrittsbalken erforderlich.
Zum Beispiel das Kopieren einer großen Datei, eine große TAR-Datei zu öffnen.
Welche Möglichkeiten empfehlen Sie Fortschrittsbalken hinzufügen Skripte zu schälen?
Lösung
Sie können dies implementieren, indem eine Linie zu überschreiben. Verwenden Sie \r
, um zurück zum Anfang der Zeile zu gehen, ohne \n
an das Terminal zu schreiben.
Write \n
wenn Sie fertig sind, die Linie zu fördern.
Mit echo -ne
an:
- nicht drucken
\n
und - erkennen Sequenzen wie
\r
zu entkommen.
Hier ist eine Demo:
echo -ne '##### (33%)\r'
sleep 1
echo -ne '############# (66%)\r'
sleep 1
echo -ne '####################### (100%)\r'
echo -ne '\n'
In einem Kommentar unten, puk erwähnt dies „versagt“, wenn Sie mit einer langen Linie beginnen und wollen dann eine kurze Zeile schreiben: In diesem Fall werden Sie müssen die Länge der langen Leitung zu überschreiben (zB mit Leerzeichen).
Andere Tipps
Sie auch wie ein Spinner zu tun:
Kann ich einen Spinner in Bash?
Sure!
i=1 sp="/-\|" echo -n ' ' while true do printf "\b${sp:i++%${#sp}:1}" done
Jedes Mal, wenn die Schleife wiederholt, zeigt es das nächste Zeichen in den sp Schnur, Umwickeln wie das Ende erreicht. (I ist die Position der das aktuelle Zeichen anzuzeigen und $ {# sp} ist die Länge des sp string).
Das \ b Zeichenfolge wird durch ein ‚Rückschritt‘ Zeichen ersetzt. Alternative, Sie könnten mit \ r spielen zu Beginn der Linie zu gehen.
Wenn Sie es wollen, verlangsamen, legte einen Schlafbefehl innerhalb der Schleife (Nach dem printf).
Ein POSIX-Äquivalent wäre:
sp='/-\|' printf ' ' while true; do printf '\b%.1s' "$sp" sp=${sp#?}${sp%???} done
Wenn Sie bereits eine Schleife haben, die viel Arbeit macht, können Sie den Anruf folgende Funktion jeder Iteration am Anfang zu aktualisieren die Spinner:
sp="/-\|" sc=0 spin() { printf "\b${sp:sc++:1}" ((sc==${#sp})) && sc=0 } endspin() { printf "\r%s\n" "$@" } until work_done; do spin some_work ... done endspin
Einige Beiträge haben gezeigt, wie man den Fortschritt des Befehls anzuzeigen. Um sie zu berechnen, müssen Sie, um zu sehen, wie viel Sie Fortschritte gemacht haben. Auf BSD-Systemen einige Befehle, wie dd (1), nehmen ein SIGINFO
Signal, und ihre Fortschritte berichten. Auf Linux-Systemen reagiert einige Befehle ähnlich wie SIGUSR1
. Wenn diese Möglichkeit vorhanden ist, können Sie Ihre Eingabe Rohr durch dd
verarbeitet, um die Anzahl von Bytes zu überwachen.
Alternativ können Sie verwenden lsof
die der Lesezeiger der Datei, Offset zu erhalten und dadurch berechnet den Fortschritt. Ich habe einen Befehl geschrieben, mit dem Namen pmonitor , dass der Fortschritt zeigt einen bestimmten Prozess oder eine Datei zu verarbeiten. Mit ihm können Sie Dinge tun, wie die folgenden.
$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%
Eine frühere Version von Linux und FreeBSD-Shell-Skripten erscheint auf meinem Blog .
Mit dem Linux-Befehl pv:
es nicht die Größe nicht wissen, ob es in der Mitte des Stromes, aber es gibt eine Geschwindigkeit und Gesamt und von dort kann man herausfinden, wie lange soll es Feedback nehmen und erhalten, so dass Sie es wissen, nicht gehangen hat.
Haben Sie eine einfache Fortschrittsbalken-Funktion, die ich neulich schrieb:
#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState($1) and totalState($2)
function ProgressBar {
# Process data
let _progress=(${1}*100/${2}*100)/100
let _done=(${_progress}*4)/10
let _left=40-$_done
# Build progressbar string lengths
_fill=$(printf "%${_done}s")
_empty=$(printf "%${_left}s")
# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /\#}${_empty// /-}] ${_progress}%%"
}
# Variables
_start=1
# This accounts as the "totalState" variable for the ProgressBar function
_end=100
# Proof of concept
for number in $(seq ${_start} ${_end})
do
sleep 0.1
ProgressBar ${number} ${_end}
done
printf '\nFinished!\n'
oder verhaken es aus,
https://github.com/fearside/ProgressBar/
Ich war auf der Suche nach etwas mehr sexy als die gewählten Antwort, so hat mein eigenes Skript.
Vorschau
Quelle
Ich habe es auf Github progress-bar.sh
progress-bar() {
local duration=${1}
already_done() { for ((done=0; done<$elapsed; done++)); do printf "▇"; done }
remaining() { for ((remain=$elapsed; remain<$duration; remain++)); do printf " "; done }
percentage() { printf "| %s%%" $(( (($elapsed)*100)/($duration)*100/100 )); }
clean_line() { printf "\r"; }
for (( elapsed=1; elapsed<=$duration; elapsed++ )); do
already_done; remaining; percentage
sleep 1
clean_line
done
clean_line
}
Verwendung
progress-bar 100
GNU tar hat eine nützliche Option, die gibt eine Funktionalität eines einfachen Fortschrittsbalken.
(...) Eine weitere verfügbare Kontrollpunkt Aktion ‚Punkt‘ (oder ‚‘). Er weist tar einen einzigen Punkt auf dem Standard-Angebot Strom zu drucken, z.
$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
Der gleiche Effekt kann erhalten:
$ tar -c --checkpoint=.1000 /var
Eine einfachere Methode, die auf meinem System mit dem pipeview (pv) Dienstprogramm funktioniert.
srcdir=$1
outfile=$2
tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print $1}'` | 7za a -si $outfile
Ich möchte auch meine eigenen Fortschritt beitragen Bar
Es erreicht Teilzeichengenauigkeit von Halb Unicode-Blöcke
mit-Code ist im Preis inbegriffen
Auf diese Weise können Sie visualisieren, dass ein Befehl noch ausgeführt wird:
while :;do echo -n .;sleep 1;done &
trap "kill $!" EXIT #Die with parent if we die prematurely
tar zxf packages.tar.gz; # or any other command here
kill $! && trap " " EXIT #Kill the loop and unset the trap or else the pid might get reassigned and we might end up killing a completely different process
Das erzeugt eine unendliche while-Schleife , die im Hintergrund ausgeführt und Echos eines „“ jede Sekunde. Dies wird .
in der Shell angezeigt werden soll. Führen Sie den tar
Befehl oder über einen Befehl, den Sie möchten. Wenn dieser Befehl ausgeführt wird beendet dann töten der letzte Job im Hintergrund laufen -. Das ist die unendliche while-Schleife
Haben Sie nicht etwas Ähnliches gesehen so ... meine sehr einfache Lösung:
#!/bin/bash
BAR='####################' # this is full bar, mine is 20 chars
for i in {1..20}; do
echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
sleep .1
done
-
echo -n
- drucken, ohne neue Zeile am Ende -
echo -e
- interpretieren Sonderzeichen beim Drucken -
"\r"
- Wagenrücklauf, ein besonderes Zeichen an den Anfang der Zeile zurück
habe ich es vor langer Zeit in einem einfachen „Hacking Video“ Code zur Simulation eingeben. ;)
Die meisten Unix-Befehle werden Sie nicht die Art von direktem Feedback geben, aus denen Sie dies tun können. Einige werden Sie die Ausgabe auf stdout geben oder stderr, dass Sie verwenden können.
Für so etwas wie Teer Sie die Option -v und Rohr des Ausgang mit einem Programm, das es liest für jede Zeile aktualisiert eine kleine Animation verwenden könnten. Als tar eine Liste von Dateien schreibt es ist das Programm entwirrt die Animation aktualisieren. Gehen Sie ein Prozent abgeschlossen würden Sie haben die Anzahl der Dateien kennen und die Zeilen zählen.
cp dieser Art der Ausgabe nicht so weit geben, wie ich weiß. Um den Fortschritt von cp überwachen würden Sie die Quell- und Zieldateien überwachen müssen und beobachten Sie die Größe des Ziels. Sie könnten ein kleines C-Programm schreiben die Verwendung von stat (2) Systemaufruf die bekommen Dateigröße. Dies würde die Größe der Quelle liest dann die Zieldatei abfragen und ein komplettes% Bar aktualisiert basierend auf der Größe der Datei Datum geschrieben.
Meine Lösung zeigt den Prozentsatz des Tarball,
ist derzeit nicht komprimiert und geschrieben wird. ich benutze das
beim Schreiben aus Dateisystem Bilder 2GB Wurzel. Du wirklich
benötigen einen Fortschrittsbalken für diese Dinge. Was ich tue, ist die Verwendung
gzip --list
die Gesamt unkomprimierte Größe des bekommen
Tarball. Daraus berechnet sich die Sperrfaktor benötigt
die Datei in 100 Teile zu unterteilen. Schließlich drucke ich ein
Prüfpunktnachricht für jeden Block. Für eine 2-GB-Datei dieses
einen Block gibt etwa 10 MB. Wenn das zu groß ist, dann können Sie
unterteilen den BLOCKING_FACTOR von 10 oder 100, aber es ist dann
härter ziemlich Ausgabe in Form eines prozentualen zu drucken.
Angenommen, Sie verwenden Bash dann können Sie mit dem Folgende Shell-Funktion
untar_progress ()
{
TARBALL=$1
BLOCKING_FACTOR=$(gzip --list ${TARBALL} |
perl -MPOSIX -ane '$.==2 && print ceil $F[1]/50688')
tar --blocking-factor=${BLOCKING_FACTOR} --checkpoint=1 \
--checkpoint-action='ttyout=Wrote %u% \r' -zxf ${TARBALL}
}
Vor allem bar ist nicht die einzige Rohrfortschrittsanzeige. Die andere (vielleicht sogar mehr bekannt) ist pv (Rohr-Viewer).
Zweitens bar und pv können beispielsweise wie folgt verwendet werden:
$ bar file1 | wc -l
$ pv file1 | wc -l
oder auch:
$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l
einen nützlichen Trick, wenn Sie den Einsatz von bar und pv in Befehlen machen wollen, die mit Dateien in Argumenten gegeben arbeiten, wie z.B. Kopie Datei1 Datei2, ist Prozess Substitution verwenden:
$ copy <(bar file1) file2
$ copy <(pv file1) file2
Prozess Substitution ist eine bash magische Sache, die temporäre Fifo Rohr Dateien / dev / fd / und verbinden stdout aus runned Prozess (in Klammern) durch dieses Rohr und kopieren Sie sieht es wie eine normale Datei (mit einer Ausnahme schafft, kann es lesen sie es nur vorwärts).
Update:
bar Befehl selbst ermöglicht auch das Kopieren. Nachdem man bar:
bar --in-file /dev/rmt/1cbn --out-file \
tape-restore.tar --size 2.4g --buffer-size 64k
Aber Prozess Substitution ist meiner Meinung nach allgemeinerer Art und Weise, es zu tun. Ein nutzt cp Programm selbst.
Ich ziehe es verwenden Dialog mit dem - Messer Param. sehr oft in .deb Paketinstallationen und andere grundlegende Konfigurationskram vieler Distributionen verwendet. So müssen Sie das Rad nicht neu erfinden ... wieder
Nur einen int-Wert setzen 1-100 @stdin. Ein grundlegendes und dummes Beispiel:
for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done
Ich habe diese / bin / Warten Sie Datei (mit chmod u + x perms) zum Kochen: P
#!/bin/bash
INIT=`/bin/date +%s`
NOW=$INIT
FUTURE=`/bin/date -d "$1" +%s`
[ $FUTURE -a $FUTURE -eq $FUTURE ] || exit
DIFF=`echo "$FUTURE - $INIT"|bc -l`
while [ $INIT -le $FUTURE -a $NOW -lt $FUTURE ]; do
NOW=`/bin/date +%s`
STEP=`echo "$NOW - $INIT"|bc -l`
SLEFT=`echo "$FUTURE - $NOW"|bc -l`
MLEFT=`echo "scale=2;$SLEFT/60"|bc -l`
TEXT="$SLEFT seconds left ($MLEFT minutes)";
TITLE="Waiting $1: $2"
sleep 1s
PTG=`echo "scale=0;$STEP * 100 / $DIFF"|bc -l`
echo $PTG| dialog --title "$TITLE" --gauge "$TEXT" 7 72
done
if [ "$2" == "" ]; then msg="Espera terminada: $1";audio="Listo";
else msg=$2;audio=$2;fi
/usr/bin/notify-send --icon=stock_appointment-reminder-excl "$msg"
espeak -v spanish "$audio"
So kann ich sagen:
Wait "34 min" "warm up the oven"
oder
Wait "dec 31" "happy new year"
Hier ist, wie es aussehen könnte
Hochladen einer Datei
[##################################################] 100% (137921 / 137921 bytes)
Warten auf einen Job zu beenden
[######################### ] 50% (15 / 30 seconds)
Einfache Funktion, die es
implementiertSie können nur copy-paste es zu Ihrem Script. Es ist nicht etwas anderes erfordert zu arbeiten.
PROGRESS_BAR_WIDTH=50 # progress bar length in characters
draw_progress_bar() {
# Arguments: current value, max value, unit of measurement (optional)
local __value=$1
local __max=$2
local __unit=${3:-""} # if unit is not supplied, do not display it
# Calculate percentage
if (( $__max < 1 )); then __max=1; fi # anti zero division protection
local __percentage=$(( 100 - ($__max*100 - $__value*100) / $__max ))
# Rescale the bar according to the progress bar width
local __num_bar=$(( $__percentage * $PROGRESS_BAR_WIDTH / 100 ))
# Draw progress bar
printf "["
for b in $(seq 1 $__num_bar); do printf "#"; done
for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH - $__num_bar ))); do printf " "; done
printf "] $__percentage%% ($__value / $__max $__unit)\r"
}
Anwendungsbeispiel
Hier haben wir eine Datei hochladen und die Fortschrittsbalken bei jeder Iteration neu zu zeichnen. Es spielt keine Rolle, welcher Job tatsächlich so lange ausgeführt wird, wie wir zwei Werte erhalten können: Max. Wert und die aktuellen Wert
Im folgenden Beispiel wird der Maximalwert ist file_size
und der aktuelle Wert wird durch eine bestimmte Funktion zugeführt und uploaded_bytes
genannt.
# Uploading a file
file_size=137921
while true; do
# Get current value of uploaded bytes
uploaded_bytes=$(some_function_that_reports_progress)
# Draw a progress bar
draw_progress_bar $uploaded_bytes $file_size "bytes"
# Check if we reached 100%
if [ $uploaded_bytes == $file_size ]; then break; fi
sleep 1 # Wait before redrawing
done
# Go to the newline at the end of upload
printf "\n"
APT Stil Fortschrittsbalken (Nicht für normale Ausgabe Pause)
EDIT: Für eine aktualisierte Version meiner Github Seite
war ich mit den Antworten auf diese Frage nicht zufrieden. Was ich persönlich auf der Suche nach einem ausgefallenen Fortschrittsbalken wurde wie von APT zu sehen ist.
hatte ich einen Blick auf dem C-Quellcode für APT und beschlossen, meine eigenen äquivalent für bash zu schreiben.
Dieser Fortschrittsbalken gut an der Unterseite des Terminals bleiben und wird mit jeder Ausgabe an das Endgerät gesendet nicht stören.
Bitte beachten Sie, dass die Bar ist derzeit auf 100 Zeichen festgelegt ist breit. Wenn Sie es auf die Größe des Terminals wollen skalieren, das ist ziemlich einfach, wie gut (die aktualisierte Version auf meiner GitHub Seite behandelt diese gut) zu erreichen.
Ich werde mein Skript hier posten. Anwendungsbeispiel:
source ./progress_bar.sh
echo "This is some output"
setup_scroll_area
sleep 1
echo "This is some output 2"
draw_progress_bar 10
sleep 1
echo "This is some output 3"
draw_progress_bar 50
sleep 1
echo "This is some output 4"
draw_progress_bar 90
sleep 1
echo "This is some output 5"
destroy_scroll_area
Das Skript (ich die Version auf meinem GitHub stattdessen wird dringend empfohlen):
#!/bin/bash
# This code was inspired by the open source C code of the APT progress bar
# http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233
#
# Usage:
# Source this script
# setup_scroll_area
# draw_progress_bar 10
# draw_progress_bar 90
# destroy_scroll_area
#
CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[30m"
COLOR_BG="\e[42m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"
function setup_scroll_area() {
lines=$(tput lines)
let lines=$lines-1
# Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
echo -en "\n"
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Set scroll region (this will place the cursor in the top left)
echo -en "\033[0;${lines}r"
# Restore cursor but ensure its inside the scrolling area
echo -en "$CODE_RESTORE_CURSOR"
echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
# Start empty progress bar
draw_progress_bar 0
}
function destroy_scroll_area() {
lines=$(tput lines)
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Set scroll region (this will place the cursor in the top left)
echo -en "\033[0;${lines}r"
# Restore cursor but ensure its inside the scrolling area
echo -en "$CODE_RESTORE_CURSOR"
echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
# We are done so clear the scroll bar
clear_progress_bar
# Scroll down a bit to avoid visual glitch when the screen area grows by one row
echo -en "\n\n"
}
function draw_progress_bar() {
percentage=$1
lines=$(tput lines)
let lines=$lines
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Move cursor position to last row
echo -en "\033[${lines};0f"
# Clear progress bar
tput el
# Draw progress bar
print_bar_text $percentage
# Restore cursor position
echo -en "$CODE_RESTORE_CURSOR"
}
function clear_progress_bar() {
lines=$(tput lines)
let lines=$lines
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Move cursor position to last row
echo -en "\033[${lines};0f"
# clear progress bar
tput el
# Restore cursor position
echo -en "$CODE_RESTORE_CURSOR"
}
function print_bar_text() {
local percentage=$1
# Prepare progress bar
let remainder=100-$percentage
progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");
# Print progress bar
if [ $1 -gt 99 ]
then
echo -ne "${progress_bar}"
else
echo -ne "${progress_bar}"
fi
}
printf_new() {
str=$1
num=$2
v=$(printf "%-${num}s" "$str")
echo -ne "${v// /$str}"
}
für mich am einfachsten zu bedienen und am besten aussehenden so weit ist Befehl pv
oder bar
wie ein Typ bereits geschrieben
zum Beispiel: Notwendigkeit, eine Sicherung der gesamten Fahrt mit dd
machen
Sie normalerweise verwenden dd if="$input_drive_path" of="$output_file_path"
mit pv
können Sie es so machen:
dd if="$input_drive_path" | pv | dd of="$output_file_path"
und der Fortschritt geht direkt als dies STDOUT
:
7.46GB 0:33:40 [3.78MB/s] [ <=> ]
, nachdem es gemacht Zusammenfassung aufkommt
15654912+0 records in
15654912+0 records out
8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
Viele Antworten beschreiben Schreiben Sie Ihre eigenen Befehle für '\r' + $some_sort_of_progress_msg
ausdrucken. Das Problem ist manchmal, dass Hunderte dieses Updates pro Sekunde heraus Druck wird den Prozess verlangsamen.
Wenn jedoch alle Ihrer Prozesse erzeugen Ausgang (zB 7z a -r newZipFile myFolder
ausgibt jeder Dateinamen wie es komprimiert), dann eine einfache, schnell, schmerzlos und anpassbare Lösung vorhanden ist.
Installieren Sie das Python-Modul tqdm
.
$ sudo pip install tqdm
$ # now have fun
$ 7z a -r -bd newZipFile myFolder | tqdm >> /dev/null
$ # if we know the expected total, we can have a bar!
$ 7z a -r -bd newZipFile myFolder | grep -o Compressing | tqdm --total $(find myFolder -type f | wc -l) >> /dev/null
Hilfe: tqdm -h
. Ein Beispiel mehr Optionen:
$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l
Als Bonus erhalten Sie auch tqdm
verwenden können Iterables in Python-Code zu wickeln.
https://github.com/tqdm/tqdm/blob/ Master / README.rst # Modul
Dies ist nur anwendbar gnome zenity verwenden. Zenity bietet eine große native Schnittstelle zu Bash-Skripte: https://help.gnome.org/users/zenity/stable/
Von Zenity Progress Bar Beispiel:
#!/bin/sh
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "20" ; sleep 1
echo "# Resetting cron jobs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "75" ; sleep 1
echo "# Rebooting system" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
--title="Update System Logs" \
--text="Scanning mail logs..." \
--percentage=0
if [ "$?" = -1 ] ; then
zenity --error \
--text="Update canceled."
fi
Ich habe eine Antwort von von wiederholten Zeichen in Shell-Skript erstellen Zeichenfolge für char wiederholen. Ich habe zwei relativ kleine bash Versionen für Skripte, die Fortschrittsbalken angezeigt werden müssen (beispielsweise eine Schleife, die durch viele Dateien geht, aber nicht nützlich für große tar-Dateien oder Kopiervorgänge). Je schneller eine besteht aus zwei Funktionen, einerseits die Saiten für Balkenanzeige vorzubereiten:
preparebar() {
# $1 - bar length
# $2 - bar char
barlen=$1
barspaces=$(printf "%*s" "$1")
barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}
und man einen Fortschrittsbalken angezeigt werden:
progressbar() {
# $1 - number (-1 for clearing the bar)
# $2 - max number
if [ $1 -eq -1 ]; then
printf "\r $barspaces\r"
else
barch=$(($1*barlen/$2))
barsp=$((barlen-barch))
printf "\r[%.${barch}s%.${barsp}s]\r" "$barchars" "$barspaces"
fi
}
Es verwendet werden könnte, wie:
preparebar 50 "#"
, die für Balken bedeutet, mit 50 "#" Zeichen Strings vorbereiten, und danach:
progressbar 35 80
wird die Anzahl der „#“ Zeichen anzuzeigen, die 35/80 Verhältnis entspricht:
[##################### ]
Beachten Sie, dass Funktion zeigt die Bar auf der gleichen Linie immer wieder, bis Sie (oder ein anderes Programm) eine neue Zeile gedruckt wird. Wenn Sie setzen -1 als erster Parameter, würde die Bar gelöscht werden:
progressbar -1 80
Die langsamere Version ist alles in einer Funktion:
progressbar() {
# $1 - number
# $2 - max number
# $3 - number of '#' characters
if [ $1 -eq -1 ]; then
printf "\r %*s\r" "$3"
else
i=$(($1*$3/$2))
j=$(($3-i))
printf "\r[%*s" "$i" | tr ' ' '#'
printf "%*s]\r" "$j"
fi
}
und es kann als (wie oben dasselbe Beispiel) verwendet werden:
progressbar 35 80 50
Wenn Sie progressbar auf stderr benötigen, fügen Sie einfach >&2
am Ende jedes printf Befehl.
Um Fortschritt der Aktivität zu zeigen, versuchen Sie die folgenden Befehle ein:
while true; do sleep 0.25 && echo -ne "\r\\" && sleep 0.25 && echo -ne "\r|" && sleep 0.25 && echo -ne "\r/" && sleep 0.25 && echo -ne "\r-"; done;
oder
while true; do sleep 0.25 && echo -ne "\rActivity: \\" && sleep 0.25 && echo -ne "\rActivity: |" && sleep 0.25 && echo -ne "\rActivity: /" && sleep 0.25 && echo -ne "\rActivity: -"; done;
oder
while true; do sleep 0.25 && echo -ne "\r" && sleep 0.25 && echo -ne "\r>" && sleep 0.25 && echo -ne "\r>>" && sleep 0.25 && echo -ne "\r>>>"; sleep 0.25 && echo -ne "\r>>>>"; done;
oder
while true; do sleep .25 && echo -ne "\r:Active:" && sleep .25 && echo -ne "\r:aCtive:" && sleep .25 && echo -ne "\r:acTive:" && sleep .25 && echo -ne "\r:actIve:" && sleep .25 && echo -ne "\r:actiVe:" && sleep .25 && echo -ne "\r:activE:"; done;
Man kann mit Flags / Variablen in der while-Schleife des Wertes / Ausmaß der Fortschritte zu überprüfen und anzuzeigen.
Basierend auf der Arbeit von Edouard Lopez, habe ich einen Fortschrittsbalken, der die Größe des Bildschirms passt, was auch immer es ist. Überprüfen Sie es heraus.
Es ist auch geschrieben auf Git Hub .
#!/bin/bash
#
# Progress bar by Adriano Pinaffo
# Available at https://github.com/adriano-pinaffo/progressbar.sh
# Inspired on work by Edouard Lopez (https://github.com/edouard-lopez/progress-bar.sh)
# Version 1.0
# Date April, 28th 2017
function error {
echo "Usage: $0 [SECONDS]"
case $1 in
1) echo "Pass one argument only"
exit 1
;;
2) echo "Parameter must be a number"
exit 2
;;
*) echo "Unknown error"
exit 999
esac
}
[[ $# -ne 1 ]] && error 1
[[ $1 =~ ^[0-9]+$ ]] || error 2
duration=${1}
barsize=$((`tput cols` - 7))
unity=$(($barsize / $duration))
increment=$(($barsize%$duration))
skip=$(($duration/($duration-$increment)))
curr_bar=0
prev_bar=
for (( elapsed=1; elapsed<=$duration; elapsed++ ))
do
# Elapsed
prev_bar=$curr_bar
let curr_bar+=$unity
[[ $increment -eq 0 ]] || {
[[ $skip -eq 1 ]] &&
{ [[ $(($elapsed%($duration/$increment))) -eq 0 ]] && let curr_bar++; } ||
{ [[ $(($elapsed%$skip)) -ne 0 ]] && let curr_bar++; }
}
[[ $elapsed -eq 1 && $increment -eq 1 && $skip -ne 1 ]] && let curr_bar++
[[ $(($barsize-$curr_bar)) -eq 1 ]] && let curr_bar++
[[ $curr_bar -lt $barsize ]] || curr_bar=$barsize
for (( filled=0; filled<=$curr_bar; filled++ )); do
printf "▇"
done
# Remaining
for (( remain=$curr_bar; remain<$barsize; remain++ )); do
printf " "
done
# Percentage
printf "| %s%%" $(( ($elapsed*100)/$duration))
# Return
sleep 1
printf "\r"
done
printf "\n"
exit 0
Genießen
Mit Vorschlägen oben aufgeführten habe ich beschlossen, meine eigenen Fortschrittsbalken zu implementieren.
#!/usr/bin/env bash
main() {
for (( i = 0; i <= 100; i=$i + 1)); do
progress_bar "$i"
sleep 0.1;
done
progress_bar "done"
exit 0
}
progress_bar() {
if [ "$1" == "done" ]; then
spinner="X"
percent_done="100"
progress_message="Done!"
new_line="\n"
else
spinner='/-\|'
percent_done="${1:-0}"
progress_message="$percent_done %"
fi
percent_none="$(( 100 - $percent_done ))"
[ "$percent_done" -gt 0 ] && local done_bar="$(printf '#%.0s' $(seq -s ' ' 1 $percent_done))"
[ "$percent_none" -gt 0 ] && local none_bar="$(printf '~%.0s' $(seq -s ' ' 1 $percent_none))"
# print the progress bar to the screen
printf "\r Progress: [%s%s] %s %s${new_line}" \
"$done_bar" \
"$none_bar" \
"${spinner:x++%${#spinner}:1}" \
"$progress_message"
}
main "$@"
habe ich eine reine Shell-Version für ein eingebettetes System nutzt:
-
/ usr / bin / dd des SIGUSR1 Signalverarbeitung Funktion.
Grundsätzlich, wenn Sie einen 'töten SIGUSR1 $ (pid_of_running_dd_process)' senden, es wird ausgegeben eine Zusammenfassung der Durchsatzgeschwindigkeit und Menge übertragen.
-
Backgrounding dd und dann regelmäßig nach Aktualisierungen abfragt und zum Erzeugen Hash-Zecken wie Old-School-FTP-Clients verwendet.
-
Die Verwendung von / dev / stdout als Ziel für Nicht-stdout freundliche Programme wie scp
Das Endergebnis können Sie einen beliebigen Dateiübertragung zu übernehmen und den aktuellen Stand bekommen, die wie Old-School-FTP ‚Hash‘ Ausgabe sieht, wo man nur einen Hash-Markierung für jeden X Bytes bekommen würde.
Dies ist kaum Produktionsqualität Code, aber Sie bekommen die Idee. Ich denke, es ist süß.
Für das, was es wert ist, die tatsächliche Byte-Zählung möglicherweise nicht korrekt in der Anzahl der Hash-Werte widerspiegeln - Sie haben eine mehr oder weniger abhängig von Rundungsfragen. Tun Sie dies nicht als Teil eines Testskript verwenden, ist es nur Augenschmaus. Und, ja, ich bin mir bewusst, diese schrecklich ineffizient ist - es ist ein Shell-Skript ist, und ich entschuldige mich nicht dafür für sie.
Beispiele mit wget, scp und tftp am Ende zur Verfügung gestellt. Es sollte mit etwas arbeiten, das aussendet Daten hat. Achten Sie darauf, / dev / stdout für Programme, die nicht stdout freundlich sind.
#!/bin/sh
#
# Copyright (C) Nathan Ramella (nar+progress-script@remix.net) 2010
# LGPLv2 license
# If you use this, send me an email to say thanks and let me know what your product
# is so I can tell all my friends I'm a big man on the internet!
progress_filter() {
local START=$(date +"%s")
local SIZE=1
local DURATION=1
local BLKSZ=51200
local TMPFILE=/tmp/tmpfile
local PROGRESS=/tmp/tftp.progress
local BYTES_LAST_CYCLE=0
local BYTES_THIS_CYCLE=0
rm -f ${PROGRESS}
dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
| grep --line-buffered -E '[[:digit:]]* bytes' \
| awk '{ print $1 }' >> ${PROGRESS} &
# Loop while the 'dd' exists. It would be 'more better' if we
# actually looked for the specific child ID of the running
# process by identifying which child process it was. If someone
# else is running dd, it will mess things up.
# My PID handling is dumb, it assumes you only have one running dd on
# the system, this should be fixed to just get the PID of the child
# process from the shell.
while [ $(pidof dd) -gt 1 ]; do
# PROTIP: You can sleep partial seconds (at least on linux)
sleep .5
# Force dd to update us on it's progress (which gets
# redirected to $PROGRESS file.
#
# dumb pid handling again
pkill -USR1 dd
local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))
# Don't print anything unless we've got 1 block or more.
# This allows for stdin/stderr interactions to occur
# without printing a hash erroneously.
# Also makes it possible for you to background 'scp',
# but still use the /dev/stdout trick _even_ if scp
# (inevitably) asks for a password.
#
# Fancy!
if [ $XFER_BLKS -gt 0 ]; then
printf "#%0.s" $(seq 0 $XFER_BLKS)
BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
fi
done
local SIZE=$(stat -c"%s" $TMPFILE)
local NOW=$(date +"%s")
if [ $NOW -eq 0 ]; then
NOW=1
fi
local DURATION=$(($NOW-$START))
local BYTES_PER_SECOND=$(( SIZE / DURATION ))
local KBPS=$((SIZE/DURATION/1024))
local MD5=$(md5sum $TMPFILE | awk '{ print $1 }')
# This function prints out ugly stuff suitable for eval()
# rather than a pretty string. This makes it a bit more
# flexible if you have a custom format (or dare I say, locale?)
printf "\nDURATION=%d\nBYTES=%d\nKBPS=%f\nMD5=%s\n" \
$DURATION \
$SIZE \
$KBPS \
$MD5
}
Beispiele:
echo "wget"
wget -q -O /dev/stdout http://www.blah.com/somefile.zip | progress_filter
echo "tftp"
tftp -l /dev/stdout -g -r something/firmware.bin 192.168.1.1 | progress_filter
echo "scp"
scp user@192.168.1.1:~/myfile.tar /dev/stdout | progress_filter
Falls Sie haben einen zeitlichen Fortschrittsbalken zeigen (von im Voraus zu wissen, die Zeit zeigt), können Sie Python verwenden wie folgt:
#!/bin/python
from time import sleep
import sys
if len(sys.argv) != 3:
print "Usage:", sys.argv[0], "<total_time>", "<progressbar_size>"
exit()
TOTTIME=float(sys.argv[1])
BARSIZE=float(sys.argv[2])
PERCRATE=100.0/TOTTIME
BARRATE=BARSIZE/TOTTIME
for i in range(int(TOTTIME)+1):
sys.stdout.write('\r')
s = "[%-"+str(int(BARSIZE))+"s] %d%% "
sys.stdout.write(s % ('='*int(BARRATE*i), int(PERCRATE*i)))
sys.stdout.flush()
SLEEPTIME = 1.0
if i == int(TOTTIME): SLEEPTIME = 0.1
sleep(SLEEPTIME)
print ""
Dann vorausgesetzt, Sie den Python-Skript als progressbar.py
gespeichert, dann ist es möglich, die Fortschrittsbalken aus dem Bash-Skript, um zu zeigen, indem Sie den folgenden Befehl ausführen:
python progressbar.py 10 50
Es wäre ein Fortschrittsbalken Größe 50
Zeichen zeigen und „running“ für 10
Sekunden.
Ich habe auf der Antwort gebaut von fearside versehen
Diese Verbindung zu einer Oracle-Datenbank der Fortschritt eines RMAN wiederherstellen abgerufen werden.
#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState($1) and totalState($2)
function ProgressBar {
# Process data
let _progress=(${1}*100/${2}*100)/100
let _done=(${_progress}*4)/10
let _left=40-$_done
# Build progressbar string lengths
_fill=$(printf "%${_done}s")
_empty=$(printf "%${_left}s")
# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%"
}
function rman_check {
sqlplus -s / as sysdba <<EOF
set heading off
set feedback off
select
round((sofar/totalwork) * 100,0) pct_done
from
v\$session_longops
where
totalwork > sofar
AND
opname NOT LIKE '%aggregate%'
AND
opname like 'RMAN%';
exit
EOF
}
# Variables
_start=1
# This accounts as the "totalState" variable for the ProgressBar function
_end=100
_rman_progress=$(rman_check)
#echo ${_rman_progress}
# Proof of concept
#for number in $(seq ${_start} ${_end})
while [ ${_rman_progress} -lt 100 ]
do
for number in _rman_progress
do
sleep 10
ProgressBar ${number} ${_end}
done
_rman_progress=$(rman_check)
done
printf '\nFinished!\n'
#!/bin/bash
function progress_bar() {
bar=""
total=10
[[ -z $1 ]] && input=0 || input=${1}
x="##"
for i in `seq 1 10`; do
if [ $i -le $input ] ;then
bar=$bar$x
else
bar="$bar "
fi
done
#pct=$((200*$input/$total % 2 + 100*$input/$total))
pct=$(($input*10))
echo -ne "Progress : [ ${bar} ] (${pct}%) \r"
sleep 1
if [ $input -eq 10 ] ;then
echo -ne '\n'
fi
}
könnte eine Funktion erstellen, die diese auf einer Skala zieht sagen 1-10 für die Anzahl der Bars:
progress_bar 1
echo "doing something ..."
progress_bar 2
echo "doing something ..."
progress_bar 3
echo "doing something ..."
progress_bar 8
echo "doing something ..."
progress_bar 10
#!/bin/bash
tot=$(wc -c /proc/$$/fd/255 | awk '/ /{print $1}')
now() {
echo $(( 100* ($(awk '/^pos:/{print $2}' < /proc/$$/fdinfo/255)-166) / (tot-166) )) "%"
}
now;
now;
now;
now;
now;
now;
now;
now;
now;
Ausgabe:
0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %
Hinweis: Wenn anstelle von 255 Sie 1 gesetzt werden Sie den Standard in ... mit 2 den Standard Monitorausgang (aber Sie müssen die Quelle ändern, um „tot“ zu der projizierten Größe der Ausgabedatei)