wake-up-neo.net

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

Wenn Sie in bash oder einer anderen Shell in * NIX Skripts erstellen, während Sie einen Befehl ausführen, der mehr als ein paar Sekunden dauert, ist eine Fortschrittsanzeige erforderlich.

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

Auf welche Weise empfehlen Sie, Fortschrittsbalken zu Shell-Skripts hinzuzufügen?

351
Tom Feiner

Sie können dies implementieren, indem Sie eine Zeile überschreiben. Verwenden Sie \r, um zum Anfang der Zeile zurückzukehren, ohne \n in das Terminal zu schreiben.

Schreiben Sie \n, wenn Sie fertig sind, um die Zeile vorzustellen.

Verwenden Sie echo -ne, um:

  1. \n und nicht drucken
  2. escape-Sequenzen wie \r zu erkennen.

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 (z. B. mit Leerzeichen) überschreiben.

639
Mitch Haile

Sie könnten auch interessiert sein an wie man einen Spinner macht :

Kann ich einen Spinner in Bash machen?

Sicher!

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

Jedes Mal, wenn die Schleife durchläuft, wird das nächste Zeichen in der Zeichenfolge angezeigt. Schnur, die sich umwickelt, wenn sie das Ende erreicht. (i ist die Position von des aktuellen anzuzeigenden Zeichens und $ {# sp} ist die Länge der sp Zeichenfolge).

Die\b-Zeichenfolge wird durch ein 'Backspace'-Zeichen ersetzt. Alternative, Sie könnten mit\r spielen, um zum Anfang der Zeile zurückzukehren.

Wenn Sie möchten, dass es langsamer wird, setzen Sie einen Schlafbefehl in die 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 über eine Schleife verfügen, die viel Arbeit leistet, können Sie die .__ aufrufen. folgende Funktion zu Beginn jeder Iteration, um die .__ zu aktualisieren. Spinner:

sp="/-\|"
sc=0
spin() {
   printf "\b${sp:sc++:1}"
   ((sc==${#sp})) && sc=0
}
endspin() {
   printf "\r%s\n" "[email protected]"
}

until work_done; do
   spin
   some_work ...
done
endspin
57
Daenyth

In einigen Beiträgen wurde 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 dd (1) ein SIGINFO-Signal und melden ihren Fortschritt. Auf Linux-Systemen reagieren einige Befehle auf SIGUSR1. Wenn diese Funktion verfügbar ist, können Sie Ihre Eingabe über dd leiten, um die Anzahl der verarbeiteten Bytes zu überwachen.

Alternativ können Sie lsof verwenden, um den Versatz des Lesezeigers der Datei zu ermitteln und dadurch den Fortschritt zu berechnen. Ich habe einen Befehl namens pmonitor geschrieben, der den Fortschritt der Verarbeitung eines angegebenen Prozesses oder einer 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-Skripts erscheint auf meinem Blog .

48

verwenden Sie den Linux-Befehl pv:

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

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

38
Seth Wegner

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 ab,
https://github.com/fearside/ProgressBar/

34
fearside

Ich suchte etwas sexyeres als die ausgewählte Antwort, und mein eigenes Skript.

Vorschau

 progress-bar.sh in action

Quelle

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

Verwendungszweck

 progress-bar 100
22
Édouard Lopez

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

(...) Eine andere verfügbare Checkpoint-Aktion ist "dot" (oder "."). Es weist tar an, einen einzelnen Punkt in dem Standardauflistungsstrom zu drucken, z.

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

Der gleiche Effekt kann erzielt werden durch:

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

Eine einfachere Methode, die auf meinem System mithilfe des Dienstprogramms pipeview (pv) ausgeführt werden kann.

srcdir=$1
outfile=$2


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

So 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

Dadurch wird eine unendliche while-Schleife erstellt, die im Hintergrund ausgeführt wird und ein "." jede Sekunde. Dies zeigt . in der Shell an. Führen Sie den Befehl tar oder einen beliebigen Befehl aus. Wenn die Ausführung dieses Befehls abgeschlossen ist, wird mit kill der letzte im Hintergrund ausgeführte Job ausgeführt. Dies ist die Endlosschleife infinite.

6
romeror

So etwas habe ich noch nicht gesehen ... 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 - ohne neue Zeile am Ende drucken
  • echo -e - interpretiert Sonderzeichen beim Drucken
  • "\r" - Wagenrücklauf, ein spezielles Zeichen, um an den Anfang der Zeile zurückzukehren

Ich habe es vor langer Zeit in einem einfachen "Hacking-Video" verwendet, um den Code zu simulieren. ;)

5
cprn

Zunächst ist der Balken nicht der einzige Fortschrittsmesser. Der andere (vielleicht sogar bekanntere) ist pv (Pipe Viewer). 

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

ein nützlicher Trick, wenn Sie in Befehlen, die mit in Argumenten angegebenen Dateien arbeiten, wie z. Kopieren Sie file1 file2, ist Prozessersetzung zu verwenden :

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

Prozessersetzung ist eine magische Sache der Bash, die temporäre Fifo-Pipe-Dateien/dev/fd/erstellt und stdout über einen ausgeführten Prozess (innerhalb von Klammern) durch diese Pipe verbindet. Die Kopie sieht wie eine gewöhnliche Datei aus (mit einer Ausnahme kann sie nur gelesen werden.) vorwärts).

Aktualisieren:

bar-Befehl selbst ermöglicht auch das Kopieren. Nach mann bar:

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

Aber Prozessersetzung ist meiner Meinung nach eine allgemeinere Art, dies zu tun. Ein cp-Programm selbst verwendet es.

4
thedk

In meiner Lösung wird der Prozentsatz des Archivs angezeigt, das derzeit nicht komprimiert und geschrieben wird. Ich verwende diese .__, wenn Sie 2 GB-Root-Dateisystem-Images ausschreiben. Sie brauchen wirklich eine Fortschrittsleiste für diese Dinge. Was ich mache, ist gzip --list, um die unkomprimierte Gesamtgröße des Tarballs abzurufen. Daraus berechne ich den benötigten Blockierungsfaktor, um die Datei in 100 Teile zu unterteilen. Zum Schluss drucke ich für jeden Block eine Checkpoint-Nachricht. Für eine 2 GB-Datei gibt this.__ etwa 10 MB pro Block aus. Wenn das zu groß ist, können Sie Den BLOCKING_FACTOR durch 10 oder 100 dividieren, aber es ist Schwieriger, eine ziemlich prozentuale Ausgabe zu drucken.

Vorausgesetzt, Sie verwenden Bash, dann 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}
}
4
Noah Spurrier

Die meisten Unix-Befehle geben Ihnen nicht die Art von direktem Feedback, von dem aus Sie dies ausführen können.

Für so etwas wie tar könnten Sie den Schalter -v verwenden und die Ausgabe an ein Programm weiterleiten, das für jede gelesene Zeile eine kleine Animation aktualisiert. Wenn tar eine Liste der Dateien ausschreibt, kann diese die Animation aktualisieren. Um einen Prozentsatz abzuschließen, müssen 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üssen Sie die Quell- und Zieldateien überwachen und die Größe des Ziels beobachten. 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, die Zieldatei abfragen und eine% komplette Leiste basierend auf der Größe der bis jetzt geschriebenen Datei aktualisieren.

Ich bevorzuge die Verwendung von dialog mit dem --gauge param. Wird sehr oft in .deb-Paketinstallationen und anderen grundlegenden Konfigurationssachen 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 dummes 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) zu Kochzwecken: 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 setzen:

Wait "34 min" "warm up the oven"

oder 

Wait "dec 31" "happy new year"

für mich am einfachsten zu verwenden und bisher am besten aussehend ist der Befehl pv oder bar, wie er von jemandem bereits geschrieben wurde

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

normalerweise verwenden Sie dd if="$input_drive_path" of="$output_file_path" 

mit pv kannst du es so machen: 

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

und der Fortschritt geht direkt zu STDOUT wie folgt:

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

nachdem es fertig ist, erscheint eine Zusammenfassung

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

Viele Antworten beschreiben das Schreiben eigener Befehle zum Ausdrucken von '\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. gibt 7z a -r newZipFile myFolder jeden Dateinamen aus, wenn er komprimiert wird), 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 auch tqdm verwenden, um iterierbare Dateien in Python-Code einzugeben.

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

2
abcdaa

Fortschrittsbalken im APT-Stil (Unterbrechen der normalen Ausgabe nicht)

 enter image description here

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 schicker Fortschrittsbalken, wie er von APT gesehen wird.

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

Dieser Fortschrittsbalken bleibt schön am unteren Rand des Terminals und beeinträchtigt keine an das Terminal gesendeten Ausgaben.

Bitte beachten Sie, dass der Balken derzeit auf 100 Zeichen festgelegt ist. Wenn Sie es auf die Größe des Terminals skalieren möchten, ist dies ebenfalls relativ einfach zu bewerkstelligen.

Ich werde mein Skript hier posten. Verwendungsbeispiel:

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 die Version auf meinem Github dringend):

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

Mit den oben aufgeführten Vorschlägen entschied ich mich, meine eigene Fortschrittsleiste 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 "[email protected]"
1
Qub3r

Basierend auf der Arbeit von Edouard Lopez habe ich einen Fortschrittsbalken erstellt, der an die Größe des Bildschirms angepasst ist. Hör zu.

 enter image description here

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
Adriano_epifas

Führen Sie die folgenden Befehle aus, 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;

Innerhalb der while-Schleife können Sie flags/variables verwenden, um den Wert/Umfang des Fortschritts zu überprüfen und anzuzeigen.

1
Sundeep Borra

Ich habe eine Antwort aus Erstellen von Zeichenfolgen aus sich wiederholenden Zeichen in Shell-Skript zum Wiederholen von Zeichen verwendet. Ich habe zwei relativ kleine bash -Versionen für Skripts, die einen Fortschrittsbalken anzeigen müssen (z. B. eine Schleife, die viele Dateien durchläuft, jedoch nicht für große Tar-Dateien oder Kopiervorgänge geeignet ist). Die schnellere besteht aus zwei Funktionen, eine zur Vorbereitung der Strings 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 eine Fortschrittsleiste 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 "#"

was bedeutet, dass Sie Strings mit 50 "#" Zeichen für den Takt vorbereiten und danach:

progressbar 35 80

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

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

Beachten Sie, dass die Funktion den Balken immer wieder in derselben Zeile anzeigt, bis Sie (oder ein anderes Programm) einen Zeilenumbruch drucken. Wenn Sie als ersten Parameter -1 setzen, 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 als (dasselbe Beispiel wie oben) verwendet werden:

progressbar 35 80 50

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

1
Zarko Zivanov

Dies gilt nur für Gnome Zenity. Zenity bietet eine großartige native Schnittstelle für Bash-Skripts: https://help.gnome.org/users/zenity/stable/

Beispiel des Fortschrittsbalkens von Zenity:

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

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

}

sie könnten eine Funktion erstellen, die dies auf einer Skala von 1-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
Mike Q

https://github.com/extensionsapp/progre.sh

40 Prozent Fortschritt schaffen: progreSh 40

 enter image description here

0
Hello World

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

  • / usr/bin/dds SIGUSR1-Signalverarbeitungsfunktion.

    Wenn Sie im Allgemeinen einen Befehl 'kill SIGUSR1 $ (pid_of_running_dd_process)' senden, wird Eine Zusammenfassung der Durchsatzgeschwindigkeit und des übertragenen Betrags ausgegeben. 

  • dd hinterlegen und dann regelmäßig nach Updates abfragen und Hash-Ticks wie alte FTP-Clients generieren.

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

Das Endergebnis ermöglicht es Ihnen, jede Dateiübertragungsoperation durchzuführen und ein Fortschrittsupdate zu erhalten, das wie eine Old-School-FTP-Hash-Ausgabe aussieht, bei der Sie für jedes X-Byte nur eine Hash-Marke erhalten.

Dies ist kaum ein Qualitätscode für die Produktion, aber Sie haben die Idee. Ich denke, es ist süß. 

Die tatsächliche Anzahl der Bytes wird unter Umständen nicht korrekt in der Anzahl der Hashes wiedergegeben. Je nach Rundungsproblemen haben Sie möglicherweise eine mehr oder weniger Zahl. Verwenden Sie dies nicht als Teil eines Testskripts, es ist nur eine Augenweide. Und ja, ich weiß, das ist furchtbar ineffizient - 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, das Daten emittiert. Stellen Sie sicher, dass Sie/dev/stdout für Programme verwenden, die nicht stdout-freundlich sind.

#!/bin/sh
#
# Copyright (C) Nathan Ramella ([email protected]) 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 [email protected]:~/myfile.tar /dev/stdout | progress_filter
0

So könnte es aussehen

Datei hochladen

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

Warten auf den Abschluss eines Jobs

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

Einfache Funktion, die es implementiert

Sie können es einfach kopieren und in Ihr Skript einfügen. Es braucht nichts anderes 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"
}

Verwendungsbeispiel

Hier laden wir eine Datei hoch und zeichnen die Fortschrittsleiste bei jeder Wiederholung neu. Es ist egal, welcher Job tatsächlich ausgeführt wird, solange wir zwei Werte erhalten können: Maximalwert und aktueller Wert.

Im Beispiel unten ist der Maximalwert file_size und der aktuelle Wert wird von einer Funktion geliefert und heißt 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"
0
Vagiz Duseev

Falls Sie einen zeitlichen Fortschrittsbalken anzeigen müssen (indem Sie die Vorführungszeit vorher 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 ""

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

python progressbar.py 10 50

Es würde einen Fortschrittsbalken mit 50 Zeichen anzeigen, der 10 Sekunden "läuft".

0
auino
#!/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 setzen, überwachen Sie den Standard in ... mit 2 den Standard out (Sie müssen jedoch die Quelle ändern, um "tot" auf die geplante Ausgabedateigröße festzulegen)

0
Zibri