Wie füge ich einem Shell-Skript einen Fortschrittsbalken hinzu?


413

Wenn Sie in Bash oder einer anderen Shell in * NIX Skripte erstellen und einen Befehl ausführen, der länger als einige Sekunden dauert, wird ein Fortschrittsbalken benötigt.

Beispiel: Kopieren einer großen Datei, Öffnen einer großen TAR-Datei.

Welche Möglichkeiten empfehlen Sie, Shell-Skripten Fortschrittsbalken hinzuzufügen?


Siehe auch stackoverflow.com/questions/12498304/… für Beispiele der Steuerlogik (Hintergrund eines Jobs und etwas tun, bis es beendet ist).
Tripleee

1
Es gibt eine Reihe von Anforderungen, die wir beim Erstellen von Skripten häufig als nützlich erachten. Protokollierung, Anzeige von Fortschritten, Farben, ausgefallenen Ausgaben usw. Ich hatte immer das Gefühl, dass es eine Art einfaches Skript-Framework geben sollte. Schließlich habe ich beschlossen, eine zu implementieren, da ich keine finden konnte. Dies könnte hilfreich sein. Es ist in purer Bash, ich meine Just Bash. github.com/SumuduLansakara/JustBash
Sumudu

Antworten:


685

Sie können dies implementieren, indem Sie eine Zeile überschreiben. Verwenden Sie \rdiese Option , um zum Zeilenanfang zurückzukehren, ohne \nauf das Terminal zu schreiben .

Schreiben Sie, \nwenn Sie fertig sind, um die Zeile voranzutreiben.

Verwenden Sie, echo -neum:

  1. nicht drucken \nund
  2. Escape-Sequenzen wie zu erkennen \r.

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 erwähnt puk, dass dies "fehlschlägt", wenn Sie mit einer langen Zeile beginnen und dann eine kurze Zeile schreiben möchten: In diesem Fall müssen Sie die Länge der langen Zeile überschreiben (z. B. mit Leerzeichen).


23
Laut der Echo- Manpage (zumindest unter MacOS X) verwendet sh / bash einen eigenen integrierten Echo- Befehl, der "-n" nicht akzeptiert. Um dasselbe zu erreichen, müssen Sie \ r setzen \ c am Ende der Zeichenfolge, statt nur \ r
Justin Jenkins

51
Die tragbare Möglichkeit, dies auszugeben, ist die Verwendung von printfanstelle von echo.
Jens

9
Für printf müssten wir dieses Format verwenden : printf "#### (50%%)\r", es würde nicht mit einfachen Anführungszeichen funktionieren und Prozentzeichen müssen maskiert werden.
Nurettin

7
Ich verstehe das nicht - akzeptiert und jede Menge Upvotes für einen Hack "Ich rate mal, wie lange dieser Vorgang auf unbekannter Hardware dauern wird"? pv ist die richtige Antwort IMO (aber Bar wird auch tun)
Stephen

19
Die Frage lautete "Wie mache ich Fortschrittsbalken?" Mit einem Beispiel für das Kopieren von Dateien. Ich habe mich auf das "Grafik" -Problem konzentriert, nicht auf die Berechnung, wie weit ein Dateikopiervorgang entfernt ist.
Mitch Haile

73

Vielleicht interessiert Sie auch, wie man einen Spinner macht :

Kann ich in Bash einen Spinner machen?

Sicher!

i=1
sp="/-\|"
echo -n ' '
while true
do
    printf "\b${sp:i++%${#sp}:1}"
done

Jedes Mal, wenn die Schleife wiederholt wird, wird das nächste Zeichen in der sp-Zeichenfolge angezeigt, das am Ende umbrochen wird. (i ist die Position des aktuell anzuzeigenden Zeichens und $ {# sp} ist die Länge der sp-Zeichenfolge).

Die Zeichenfolge \ b wird durch ein Rücktastezeichen ersetzt. Alternativ können Sie mit \ r spielen, um zum Zeilenanfang zurückzukehren.

Wenn Sie möchten, dass es langsamer wird, fügen Sie einen Schlafbefehl in die Schleife ein (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 leistet, können Sie zu Beginn jeder Iteration die folgende Funktion aufrufen, um den Spinner zu aktualisieren:

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

15
Viel kürzere Version, vollständig portabel *: while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*: sleeperfordert möglicherweise eher Ints als Dezimalstellen)
Adam Katz

1
@ Daenyth. Vielen Dank. Wo sollten wir den Befehl aufrufen, den wir benötigen, um den Fortschritt mit dem vorherigen Code zu beobachten?
Goro

@goro: In der some_work ...obigen Zeile; Eine ausführlichere Diskussion, die auf dieser hilfreichen Antwort aufbaut, und Adam Katz 'hilfreicher Kommentar - mit Schwerpunkt auf der POSIX-Konformität - finden Sie hier .
mklement0

@AdamKatz: Das ist eine hilfreiche, tragbare Vereinfachung, aber um Daenyths Ansatz anzupassen, muss der Spinner \beher auf als basieren \r, da er sonst nur am Anfang einer Zeile funktioniert: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- oder, wenn der Cursor hinter dem Spinner angezeigt wird ist unerwünscht:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0

1
@kaushal - Strg + C stoppt es manuell. Wenn Sie einen Hintergrundjob haben, können Sie seine PID ( job=$!) speichern und dann ausführen while kill -0 $job 2>/dev/null;do …, zum Beispiel:sleep 15 & job=$!; while kill -0 $job 2>/dev/null; do for s in / - \\ \|; do printf "\r$s"; sleep .1; done; done
Adam Katz

48

Einige Beiträge haben gezeigt, wie der Fortschritt des Befehls angezeigt wird. Um es zu berechnen, müssen Sie sehen, wie weit Sie fortgeschritten sind. Auf BSD-Systemen akzeptieren einige Befehle, wie z. B. dd (1), ein SIGINFOSignal und melden ihren Fortschritt. Auf Linux-Systemen reagieren einige Befehle ähnlich SIGUSR1. Wenn diese Funktion verfügbar ist, können Sie Ihre Eingabe weiterleiten dd, um die Anzahl der verarbeiteten Bytes zu überwachen.

Alternativ können Sie lsofden Versatz des Lesezeigers der Datei ermitteln und damit den Fortschritt berechnen. Ich habe einen Befehl namens pmonitor geschrieben , der den Fortschritt der Verarbeitung eines bestimmten Prozesses oder einer bestimmten Datei anzeigt. Damit können Sie Dinge wie die folgenden tun.

$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%

Eine frühere Version von Linux- und FreeBSD-Shell-Skripten wird in meinem Blog angezeigt .


Das ist großartig, ich vergesse immer, Dinge durch pv zu leiten :-) Ich denke, mein "stat" -Befehl funktioniert etwas anders, meine (Linux-) Version dieses Skripts: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b
unhammer

Toller Beitrag, liebe es immer, wenn es awkins Spiel kommt!
ShellFish

Das ist toll! Danke für das tolle Drehbuch!
Thebeagle

47

Ich habe eine einfache Fortschrittsbalkenfunktion, die ich neulich geschrieben habe:

#!/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 schnappen Sie es sich unter
https://github.com/fearside/ProgressBar/


Können Sie bitte die Zeile unter 1.2.1.1 erklären? Führen Sie eine sed-Substitution mit den Variablen _fill und _empty durch? Ich bin verwirrt.
Chirag

Anstatt sed zu verwenden, verwende ich bash internes "Substring Replacement", da dies eine einfache Aufgabe ist, bevorzuge ich die internen Funktionen von bash für diese Art von Arbeit. Code sieht auch besser aus. :-) Überprüfen Sie hier tldp.org/LDP/abs/html/string-manipulation.html und suchen Sie nach Ersatz für Teilzeichenfolgen .
Fearside

und $ {_ fill} wird als $ {_ done} Anzahl von Leerzeichen zugewiesen. Dies ist schön. Großartiger Job, Mann. Ich werde dies definitiv in all meinen Bash-Skripten verwenden haha
Chirag

Tolle Arbeit @fearside! Ich habe eine kleine Änderung vorgenommen, um zu überspringen, wenn sich _progress nicht vom letzten Wert geändert hat, um die Geschwindigkeit zu verbessern. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

Süss. Das Ändern des Strichs durch das Rechteck verleiht ihm ein professionelleres Erscheinungsbild:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI

44

Verwenden Sie den Linux-Befehl pv:

http://linux.die.net/man/1/pv

Es kennt die Größe nicht, wenn es sich in der Mitte des Streams befindet, aber es gibt eine Geschwindigkeit und eine Gesamtmenge an. Von dort aus können Sie herausfinden, wie lange es dauern sollte, und Feedback erhalten, damit Sie wissen, dass es nicht hängen geblieben ist.


31

Ich suchte nach etwas Sexyem als der ausgewählten Antwort, ebenso wie mein eigenes Skript.

Vorschau

progress-bar.sh in Aktion

Quelle

Ich habe es auf Github gelegtprogress-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
}

Verwendungszweck

 progress-bar 100

1
Ich verstehe nicht, wie dies in eine Verarbeitung integriert wird, bei der die Länge des Prozesses nicht bekannt ist. So stoppen Sie den Fortschrittsbalken, wenn mein Vorgang früher abgeschlossen wurde, z. B. zum Entpacken einer Datei.
Januar

Ich denke, Verwendung sollte seinprogress-bar 100
Jirarium

Attraktiver Fortschritt. Wie kann es an eine Funktion gebunden werden, die längere Aktionen auf Remote-Servern über ssh verarbeitet? Ich meine, wie ist es möglich, den Zeitpunkt eines Upgrades (zum Beispiel) auf Remote-Servern zu messen?
gesichtslos

1
@faceless es ist nicht im Umfang dieses Codes Sie geben die Zeit und es zählt herunter
Édouard Lopez

1
@Fusion Es ist ein Unicode-Zeichen (U + 2587 LOWER SEVEN EIGHTHS BLOCK), das für moderne Shell sicher sein sollte. Probieren Sie Ihre Umgebung aus
Édouard Lopez

18

GNU tar verfügt über eine nützliche Option, die die Funktionalität eines einfachen Fortschrittsbalkens bietet.

(...) Eine weitere verfügbare Checkpoint-Aktion ist 'dot' (oder '.'). Es weist tar an, einen einzelnen Punkt auf dem Standard-Listing-Stream zu drucken, z.

$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...

Der gleiche Effekt kann erzielt werden durch:

$ tar -c --checkpoint=.1000 /var

+1 für den einfachsten Ansatz! Wenn keine Punkte gedruckt werden, versuchen Sie beispielsweise, die Anzahl zu verringern --checkpoint=.10. Es funktioniert auch gut beim Extrahieren mit tar -xz.
Noam Manos

13

Eine einfachere Methode, die auf meinem System mit dem Dienstprogramm pipeview (pv) funktioniert.

srcdir=$1
outfile=$2


tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print $1}'` | 7za a -si $outfile

13

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 $iWert bestimmt wird, dh wie viel des Fortschrittsbalkens angezeigt werden soll. Im obigen Beispiel habe ich es nur in einer forSchleife inkrementieren lassen , um das Prinzip zu veranschaulichen, aber eine reale Anwendung würde eine Endlosschleife verwenden und die $iVariable bei jeder Iteration berechnen . Für diese Berechnung werden die folgenden Zutaten benötigt:

  1. wie viel Arbeit gibt es zu tun
  2. Wie viel Arbeit wurde bisher geleistet?

Falls cpes 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 $BARDynamik 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.


1
Für diejenigen, die das einfachste Zeug wollen, habe ich gerade mein mit cprn erster Antwort gemacht. Es ist eine sehr einfache Fortschrittsbalken in einer Funktion , die einige dumme Proportionalitätsregel verwenden , um die Bar zu ziehen: pastebin.com/9imhRLYX
YCN-


9

Fortschrittsbalken im APT-Stil (unterbricht die normale Ausgabe nicht)

Geben Sie hier die Bildbeschreibung ein

BEARBEITEN: Eine aktualisierte Version finden Sie auf meiner Github-Seite

Ich war mit den Antworten auf diese Frage nicht zufrieden. Was ich persönlich gesucht habe, war ein ausgefallener Fortschrittsbalken, wie APT sieht.

Ich habe mir den C-Quellcode für APT angesehen und beschlossen, mein eigenes Äquivalent für Bash zu schreiben.

Dieser Fortschrittsbalken bleibt gut am unteren Rand des Terminals und stört keine an das Terminal gesendeten Ausgaben.

Bitte beachten Sie, dass die Leiste derzeit auf 100 Zeichen Breite festgelegt ist. Wenn Sie es auf die Größe des Terminals skalieren möchten, ist dies ebenfalls recht einfach (die aktualisierte Version auf meiner Github-Seite behandelt dies gut).

Ich werde mein Skript hier veröffentlichen. 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 empfehle dringend die Version auf meinem Github):

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

7

Auf diese Weise können Sie sich vorstellen, 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

Dadurch wird eine Endlos-while-Schleife erstellt , die im Hintergrund ausgeführt wird und ein "" wiedergibt. jede Sekunde. Dies wird .in der Shell angezeigt . Führen Sie den tarBefehl oder einen beliebigen Befehl aus. Wenn dass beendet Befehl ausgeführt wird dann töten den letzten Job im Hintergrund laufen - was das ist unendlich while - Schleife .


Könnte während der Ausführung nicht ein anderer Job im Hintergrund gestartet werden und möglicherweise anstelle der Fortschrittsschleife getötet werden?
Centimane

Ich denke, die Idee ist, dass Sie dies in ein Skript einfügen würden, sodass dies nur einen Ausgang dieses Skripts einfangen würde.
Iguananaut

1
Ich liebe diesen Befehl, ich verwende ihn in meinen Dateien. Ich bin nur ein bisschen unruhig, da ich nicht wirklich verstehe, wie es funktioniert. Die erste und dritte Zeile sind leichter zu verstehen, aber ich bin mir immer noch nicht sicher. Ich weiß, dass dies eine alte Antwort ist, aber gibt es eine Möglichkeit, eine andere Erklärung zu erhalten, die sich an Neulinge beim Programmieren richtet
Felipe

1
Dies ist die EINZIGE wahre Antwort, bei der andere nur Scripting 101-Fortschrittsbalken für Spielzeug verwenden, die nichts bedeuten und für echte, einmalige, nicht nachverfolgbare (fast ALLE) Programme keine Verwendung finden. Vielen Dank.
bekce

@Felipe, Die while-Schleife ist ein Hintergrundprozess. Das $! In der ersten Trap wird die Prozess-ID dieses Hintergrundprozesses erfasst und sichergestellt, dass beim Beenden des aktuellen / übergeordneten Prozesses auch der Hintergrundprozess stirbt und nicht hängen bleibt. Die kill-Anweisung beendet den Hintergrundprozess nur, wenn Ihr langer Befehl oder Ihre langen Befehle enden.
Floydn

7

So könnte es aussehen

Hochladen einer Datei

[##################################################] 100% (137921 / 137921 bytes)

Warten auf den Abschluss eines Auftrags

[#########################                         ] 50% (15 / 30 seconds)

Einfache Funktion, die es implementiert

Sie können es einfach kopieren und in Ihr Skript einfügen. Es erfordert nichts anderes, um zu funktionieren.

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 laden wir eine Datei hoch und zeichnen den Fortschrittsbalken bei jeder Iteration neu. Es spielt keine Rolle, welcher Job tatsächlich ausgeführt wird, solange wir zwei Werte erhalten können: Maximalwert und aktueller Wert.

Im folgenden Beispiel ist der Maximalwert file_sizeund der aktuelle Wert wird von einer Funktion geliefert und aufgerufen uploaded_bytes.

# 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"

Ordentliche und einfache Funktion. Vielen Dank!
Andreas Kraft

Das ist was ich suche! Vielen Dank :)
wajdi_jurry

4

Die meisten Unix-Befehle geben Ihnen nicht die Art von direktem Feedback, von dem Sie dies tun können. Einige geben Ihnen eine Ausgabe auf stdout oder stderr, die Sie verwenden können.

Für so etwas wie tar können Sie den Schalter -v verwenden und die Ausgabe an ein Programm weiterleiten, das für jede gelesene Zeile eine kleine Animation aktualisiert. Während tar eine Liste der entpackten Dateien schreibt, kann das Programm die Animation aktualisieren. Um einen Prozentsatz zu vervollständigen, müssten Sie die Anzahl der Dateien kennen und die Zeilen zählen.

cp gibt diese Art von Ausgabe meines Wissens nicht aus. Um den Fortschritt von cp zu überwachen, müssten Sie die Quell- und Zieldateien überwachen und die Größe des Ziels überwachen. Sie können ein kleines c-Programm mit dem Systemaufruf stat (2) schreiben , um die Dateigröße zu ermitteln. Dies würde die Größe der Quelle lesen, dann die Zieldatei abfragen und einen% vollständigen Balken basierend auf der Größe der bisher geschriebenen Datei aktualisieren.


4

Meine Lösung zeigt den Prozentsatz des Tarballs an, der gerade unkomprimiert und geschrieben wird. Ich benutze dies beim Schreiben von 2 GB Root-Dateisystem-Images. Sie brauchen wirklich einen Fortschrittsbalken für diese Dinge. Was ich tue, ist zu verwenden gzip --list, um die gesamte unkomprimierte Größe des Tarballs zu erhalten. Daraus berechne ich den Blockierungsfaktor, der benötigt wird, um die Datei in 100 Teile zu teilen. Schließlich drucke ich für jeden Block eine Prüfpunktnachricht. Für eine 2-GB-Datei ergibt dies etwa 10 MB pro Block. Wenn das zu groß ist, können Sie den BLOCKING_FACTOR durch 10 oder 100 teilen, aber dann ist es schwieriger, eine hübsche Ausgabe in Prozent zu drucken.

Angenommen, Sie verwenden Bash, können Sie die folgende Shell-Funktion verwenden

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}
}

Gute Lösung, aber wie geht das, wenn Sie ein Verzeichnis komprimieren möchten?
Samir Sadek

4

Erstens ist der Balken nicht der einzige Rohrfortschrittsmesser. Der andere (vielleicht sogar noch bekanntere) ist pv (Pipe Viewer).

Zweitens können bar und pv zum Beispiel so 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

Ein nützlicher Trick, wenn Sie bar und pv in Befehlen verwenden möchten, die mit Dateien arbeiten, die in Argumenten angegeben sind, z. B. copy file1 file2, ist die Verwendung der Prozessersetzung :

$ copy <(bar file1) file2
$ copy <(pv file1) file2

Die Prozessersetzung ist eine magische Sache, die temporäre FIFO-Pipe-Dateien / dev / fd / erstellt und stdout aus dem ausgeführten Prozess (in Klammern) über diese Pipe verbindet, und die Kopie sieht sie wie eine normale Datei aus (mit einer Ausnahme kann sie nur gelesen werden) vorwärts).

Aktualisieren:

Der Befehl bar selbst ermöglicht auch das Kopieren. After Man Bar:

bar --in-file /dev/rmt/1cbn --out-file \
     tape-restore.tar --size 2.4g --buffer-size 64k

Aber Prozesssubstitution ist meiner Meinung nach eine allgemeinere Methode, um dies zu tun. Und es verwendet das CP-Programm selbst.


3

Ich bevorzuge den Dialog mit dem Parameter --gauge. Wird sehr häufig in .deb-Paketinstallationen und anderen grundlegenden Konfigurationsmaterialien vieler Distributionen verwendet. Sie müssen das Rad also nicht neu erfinden

Geben Sie einfach einen int-Wert von 1 bis 100 @stdin ein. Ein einfaches und albernes Beispiel:

for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done

Ich habe diese / bin / Wait- 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"

Also kann ich sagen:

Wait "34 min" "warm up the oven"

oder

Wait "dec 31" "happy new year"


2

Für mich ist das Kommando am einfachsten zu bedienen und am besten auszusehen, pvoder barwie ein Typ bereits geschrieben hat

Beispiel: Sie müssen eine Sicherungskopie des gesamten Laufwerks mit erstellen dd

normalerweise benutzt du dd if="$input_drive_path" of="$output_file_path"

mit pvdir kannst du es so machen:

dd if="$input_drive_path" | pv | dd of="$output_file_path"

und der Fortschritt geht direkt STDOUTwie folgt vor:

    7.46GB 0:33:40 [3.78MB/s] [  <=>                                            ]

Nachdem dies erledigt ist, wird eine Zusammenfassung angezeigt

    15654912+0 records in
    15654912+0 records out
    8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s

Können Sie den Fortschritt verschiedener Prozesse verwenden pvoder barvisualisieren, z. B. den Countdown des Timers, die Position in einer Textdatei, die Installation Ihrer App, die Einrichtung der Laufzeit usw.?
cprn

2

Viele Antworten beschreiben das Schreiben eigener Befehle zum Ausdrucken '\r' + $some_sort_of_progress_msg. Das Problem ist manchmal, dass das Ausdrucken von Hunderten dieser Updates pro Sekunde den Prozess verlangsamt.

Wenn jedoch einer Ihrer Prozesse eine Ausgabe erzeugt (z. B. 7z a -r newZipFile myFolderwird jeder Dateiname beim Komprimieren ausgegeben), gibt es eine einfachere, schnellere, schmerzlosere und anpassbare Lösung.

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 mit mehr Optionen:

$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l

Als Bonus können Sie tqdmIterables auch in Python-Code einschließen.

https://github.com/tqdm/tqdm/blob/master/README.rst#module


Ich denke nicht, dass Ihr Beispiel mit "mehr Optionen" funktioniert. Es scheint den tqdmSTDOUT wc -ldurch ein Rohr zu leiten . Sie wollen dem wahrscheinlich entkommen.
cprn

1
@cprn tqdmzeigt den Fortschritt an, STDERRwährend die Eingabe STDINan weitergeleitet wird STDOUT. In diesem Fall wc -lwürde dieselbe Eingabe empfangen, als wäre sie tqdmnicht enthalten.
casper.dcl

Ah, macht jetzt Sinn. Danke für die Erklärung.
cprn

2

Basierend auf der Arbeit von Edouard Lopez habe ich einen Fortschrittsbalken erstellt, der der Größe des Bildschirms entspricht, unabhängig davon, um welchen es sich handelt. Hör zu.

Geben Sie hier die Bildbeschreibung ein

Es ist auch auf Git Hub veröffentlicht .

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



1

Dies gilt nur mit Gnome Zenity. Zenity bietet eine großartige native Oberfläche für Bash-Skripte: https://help.gnome.org/users/zenity/stable/

Aus dem 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

1

Ich habe eine Antwort aus Erstellen einer Zeichenfolge wiederholter Zeichen im Shell-Skript zum Wiederholen von Zeichen verwendet . Ich habe zwei relativ kleine Bash- Versionen für Skripte, die einen Fortschrittsbalken anzeigen müssen (zum Beispiel eine Schleife, die viele Dateien durchläuft, aber für große TAR-Dateien oder Kopiervorgänge nicht nützlich ist). Die schnellere besteht aus zwei Funktionen, eine zum Vorbereiten der Zeichenfolgen für die Balkenanzeige:

preparebar() {
# $1 - bar length
# $2 - bar char
    barlen=$1
    barspaces=$(printf "%*s" "$1")
    barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}

und eine, um einen Fortschrittsbalken anzuzeigen:

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 könnte verwendet werden als:

preparebar 50 "#"

Dies bedeutet, dass Sie Zeichenfolgen für Balken mit 50 "#" Zeichen vorbereiten und danach:

progressbar 35 80

zeigt die Anzahl der "#" Zeichen an, die dem Verhältnis 35/80 entsprechen:

[#####################                             ]

Beachten Sie, dass die Funktion die Leiste immer wieder in derselben Zeile anzeigt, bis Sie (oder ein anderes Programm) eine neue Zeile drucken. Wenn Sie -1 als ersten Parameter eingeben, wird der Balken gelöscht:

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 verwendet werden als (das gleiche Beispiel wie oben):

progressbar 35 80 50

Wenn Sie einen Fortschrittsbalken für stderr benötigen, fügen Sie ihn einfach >&2am Ende jedes printf-Befehls hinzu.


1

Versuchen Sie die folgenden Befehle, um den Fortschritt der Aktivität anzuzeigen:

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 Flags / Variablen innerhalb der while-Schleife verwenden, um den Wert / das Ausmaß des Fortschritts zu überprüfen und anzuzeigen.


1

Mit den oben aufgeführten Vorschlägen habe ich beschlossen, meinen 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 "$@"

1
Nett! Um es percent_none="$(( 100 - "$percent_done" ))"zum percent_none="$(( 100 - $percent_done))"
Laufen

0

Ich habe eine reine Shell-Version für ein eingebettetes System erstellt und dabei Folgendes genutzt:

  • Die SIGUSR1-Signalverarbeitungsfunktion von / usr / bin / dd.

    Wenn Sie ein 'kill SIGUSR1 $ (pid_of_running_dd_process)' senden, wird grundsätzlich eine Zusammenfassung der Durchsatzgeschwindigkeit und des übertragenen Betrags ausgegeben.

  • dd hinterlegen und dann regelmäßig nach Updates abfragen und Hash-Ticks generieren, wie es früher bei FTP-Clients der alten Schule der Fall war.

  • Verwenden von / dev / stdout als Ziel für nicht stdout-freundliche Programme wie scp

Mit dem Endergebnis können Sie jede Dateiübertragungsoperation ausführen und eine Fortschrittsaktualisierung erhalten, die wie eine FTP-Hash-Ausgabe der alten Schule aussieht, bei der Sie nur für jedes X-Byte eine Hash-Markierung erhalten.

Dies ist kaum Produktionsqualitätscode, aber Sie bekommen die Idee. Ich denke, es ist süß.

Für das, was es wert ist, spiegelt sich die tatsächliche Anzahl der Bytes möglicherweise nicht richtig in der Anzahl der Hashes wider - je nach Rundungsproblemen haben Sie möglicherweise mehr oder weniger. Verwenden Sie dies nicht als Teil eines Testskripts, es ist nur eine Augenweide. Und ja, ich bin mir bewusst, dass dies furchtbar ineffizient ist - es ist ein Shell-Skript und ich entschuldige mich nicht dafür.

Beispiele mit wget, scp und tftp am Ende. Es sollte mit allem funktionieren, was Daten ausgibt. Stellen Sie sicher, dass Sie / dev / stdout für Programme verwenden, 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

Anständige Idee, solange Sie die Dateigröße im Voraus haben, können Sie auf diese Weise einen Mehrwert als pv bieten, aber blind signalisieren, dass dies pidof ddbeängstigend ist.

Ich habe versucht, das mit '# Meine PID-Behandlung ist dumm' aufzurufen
Synthesizerpatel

Sie können vielleicht erfassen $!aus ddund warten auf [[ -e /proc/${DD_PID} ]].

0

Falls Sie einen zeitlichen Fortschrittsbalken anzeigen müssen (indem Sie die Anzeigezeit im Voraus kennen), können Sie Python wie folgt verwenden:

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

Angenommen, Sie haben das Python-Skript als gespeichert progressbar.py, können Sie den Fortschrittsbalken Ihres Bash-Skripts anzeigen, indem Sie den folgenden Befehl ausführen:

python progressbar.py 10 50

Es würde 50Zeichen in der Größe eines Fortschrittsbalkens anzeigen und für 10Sekunden "laufen" .


0

Ich habe auf der Antwort von Fearside aufgebaut

Dadurch wird eine Verbindung zu einer Oracle-Datenbank hergestellt, um den Fortschritt einer RMAN-Wiederherstellung abzurufen.

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

0
#!/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 dies auf einer Skala von 1 bis 10 für die Anzahl der Balken zeichnet:

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

0
#!/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 Sie anstelle von 255 1 eingeben, überwachen Sie den Standard in ... und 2 den Standardausgang (Sie müssen jedoch die Quelle ändern, um "tot" auf die projizierte Ausgabedateigröße festzulegen).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.