Comment ajouter une barre de progression à un script shell?

413

Lors de l'écriture de scripts dans bash ou tout autre shell dans * NIX, lors de l'exécution d'une commande qui prendra plus de quelques secondes, une barre de progression est nécessaire.

Par exemple, copier un gros fichier, ouvrir un gros fichier tar.

Quelles méthodes recommandez-vous pour ajouter des barres de progression aux scripts shell?

Tom Feiner
la source
Voir aussi stackoverflow.com/questions/12498304/… pour des exemples de la logique de contrôle (mettez en arrière-plan un travail et faites quelque chose jusqu'à la fin).
tripleee
1
Il existe un ensemble d'exigences que nous trouvons fréquemment utiles lors de l'écriture de scripts. journalisation, affichage de la progression, des couleurs, des sorties fantaisie, etc. J'ai toujours pensé qu'il devrait y avoir une sorte de cadre de script simple. J'ai finalement décidé d'en implémenter un car je n'en ai trouvé aucun. Vous pourriez trouver cela utile. C'est en pur bash, je veux dire Just Bash. github.com/SumuduLansakara/JustBash
Sumudu
Cela ne devrait-il pas être déplacé vers unix.stackexchange.com ?
Ethan

Réponses:

685

Vous pouvez implémenter cela en écrasant une ligne. Permet \rde revenir au début de la ligne sans écrire \nsur le terminal.

Écrivez \nquand vous avez terminé pour faire avancer la ligne.

Utilisez echo -nepour:

  1. pas imprimer \net
  2. pour reconnaître des séquences d'échappement comme \r.

Voici une démo:

echo -ne '#####                     (33%)\r'
sleep 1
echo -ne '#############             (66%)\r'
sleep 1
echo -ne '#######################   (100%)\r'
echo -ne '\n'

Dans un commentaire ci-dessous, puk mentionne que cela "échoue" si vous commencez par une longue ligne et que vous souhaitez ensuite écrire une ligne courte: dans ce cas, vous devrez écraser la longueur de la longue ligne (par exemple, avec des espaces).

Mitch Haile
la source
23
Selon la page de manuel echo (au moins sur MacOS X) sh / bash utilise leur propre commande echo intégrée qui n'accepte pas "-n" ... donc pour accomplir la même chose que vous devez mettre \ r \ c à la fin de la chaîne, au lieu de simplement \ r
Justin Jenkins
51
La manière portable de produire ceci est d'utiliser printfau lieu de echo.
Jens
9
pour printf, nous devrions utiliser ce format printf "#### (50%%)\r":, cela ne fonctionnerait pas avec des guillemets simples et le signe de pourcentage doit être échappé.
nurettin
7
Je ne reçois pas cela - accepté et des tas de votes positifs pour un "je devine combien de temps cette opération prendra sur du matériel inconnu"? pv est la bonne réponse OMI (mais la barre fera aussi)
Stephen
19
La question était "Comment faire les barres de progression" avec un exemple de copie de fichiers. Je me suis concentré sur le problème "graphique", pas sur le calcul de la distance parcourue par une opération de copie de fichier.
Mitch Haile,
73

Vous pouvez également être intéressé par la façon de faire un spinner :

Puis-je faire un spinner dans Bash?

Sûr!

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

Chaque fois que la boucle itère, elle affiche le caractère suivant dans la chaîne sp, se déplaçant à mesure qu'elle atteint la fin. (i est la position du caractère courant à afficher et $ {# sp} est la longueur de la chaîne sp).

La chaîne \ b est remplacée par un caractère de «retour arrière». Alternativement, vous pouvez jouer avec \ r pour revenir au début de la ligne.

Si vous voulez qu'il ralentisse, mettez une commande sleep à l'intérieur de la boucle (après le printf).

Un équivalent POSIX serait:

sp='/-\|'
printf ' '
while true; do
    printf '\b%.1s' "$sp"
    sp=${sp#?}${sp%???}
done

Si vous avez déjà une boucle qui fait beaucoup de travail, vous pouvez appeler la fonction suivante au début de chaque itération pour mettre à jour le 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
Daenyth
la source
15
Version beaucoup plus courte, entièrement portable *: while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*: sleeppeut nécessiter des points plutôt que des décimales)
Adam Katz
1
@Daenyth. Merci. Veuillez où nous devrions appeler la commande dont nous avons besoin pour voir si elle progresse en utilisant le code précédent?
goro
@goro: Dans la some_work ...ligne ci-dessus; une discussion plus détaillée qui s'appuie sur cette réponse utile et le commentaire utile d'Adam Katz - en mettant l'accent sur la conformité POSIX - peut être trouvée ici .
mklement0
@AdamKatz: C'est une simplification utile et portable, mais pour correspondre à l'approche de Daenyth, le spinner doit être basé sur \bplutôt que \r, car il ne fonctionnera autrement qu'au tout début d'une ligne: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- ou, si vous affichez le curseur derrière le spinner est indésirable:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0
1
@kaushal - Ctrl + C l'arrêtera manuellement. Si vous avez un travail en arrière-plan, vous pouvez stocker son PID ( job=$!) puis exécuter while kill -0 $job 2>/dev/null;do …, par exemple: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

Certains articles ont montré comment afficher la progression de la commande. Pour le calculer, vous devrez voir combien vous avez progressé. Sur les systèmes BSD, certaines commandes, telles que dd (1), acceptent un SIGINFOsignal et signalent leur progression. Sur les systèmes Linux, certaines commandes répondent de la même manière SIGUSR1. Si cette fonctionnalité est disponible, vous pouvez diriger votre entrée viadd pour surveiller le nombre d'octets traités.

Vous pouvez également utiliser lsofpour obtenir le décalage du pointeur de lecture du fichier et ainsi calculer la progression. J'ai écrit une commande, nommée pmonitor , qui affiche la progression du traitement d'un processus ou d'un fichier spécifié. Avec lui, vous pouvez faire des choses, comme les suivantes.

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

Une version antérieure des scripts shell Linux et FreeBSD apparaît sur mon blog .

Diomidis Spinellis
la source
C'est génial, j'oublie toujours de diriger les choses via pv :-) Je pense que ma commande "stat" fonctionne un peu différemment, ma version (Linux) de ce script: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b
unhammer
Excellent article, j'adore toujours quand il awkest en jeu!
ShellFish
C'est bien! Merci pour le script génial!
thebeagle
47

J'ai une fonction de barre de progression facile que j'ai écrite l'autre jour:

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

Ou accrochez-le,
https://github.com/fearside/ProgressBar/

peur
la source
pouvez-vous expliquer la ligne sous 1.2.1.1 s'il vous plaît? Effectuez-vous une substitution sed avec les variables _fill et _empty? Je suis confus.
Chirag
Au lieu d'utiliser sed, im utilisant bash "Substring Replacement" interne, car c'est un travail facile, je préfère utiliser les fonctions internes de bash pour ce type de travail. Le code est également plus joli. :-) Vérifiez ici tldp.org/LDP/abs/html/string-manipulation.html et recherchez le remplacement de la sous-chaîne.
fearside
et $ {_ fill} est assigné comme $ {_ done} nombre d'espaces. C'est beau. Grand homme de travail. Je vais certainement l'utiliser dans tous mes scripts bash haha
Chirag
Excellent travail @fearside! J'ai fait un petit ajustement pour sauter quand _progress n'a pas changé depuis la dernière valeur, pour améliorer la vitesse. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs
Doux. Le changement de tiret par rectangle lui donne un aspect et une sensation plus professionnels:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI
44

utilisez la commande linux pv:

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

il ne connaît pas la taille s'il est au milieu du flux, mais il donne une vitesse et un total et à partir de là, vous pouvez déterminer combien de temps cela devrait prendre et obtenir des commentaires afin que vous sachiez qu'il n'a pas bloqué.

Seth Wegner
la source
32

Je cherchais quelque chose de plus sexy que la réponse choisie, tout comme mon propre script.

Aperçu

progress-bar.sh en action

La source

Je l'ai mis sur githubprogress-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
}

Usage

 progress-bar 100
Édouard Lopez
la source
1
Je ne comprends pas comment cela est intégré dans certains traitements où la durée du processus n'est pas connue. Comment arrêter la barre de progression si mon processus s'est terminé plus tôt, par exemple pour décompresser un fichier.
janvier
Je pense que l'utilisation devrait êtreprogress-bar 100
jirarium
Des progrès attractifs en effet. Comment peut-il être lié à une fonction qui traite une action prolongée sur des serveurs distants via ssh? Je veux dire, comment est-il possible de mesurer le calendrier d'une mise à niveau (par exemple) sur des serveurs distants?
2018 sans visage
1
@faceless ce n'est pas dans la portée de ce code que vous fournissez le temps et ça compte à rebours
Édouard Lopez
1
@Fusion, c'est un caractère unicode (U + 2587 BLOC SEVEN EIGHTHS) qui devrait être sans danger pour le shell moderne. Essayez vos envies
Édouard Lopez
18

GNU tar a une option utile qui donne la fonctionnalité d'une simple barre de progression.

(...) Une autre action de point de contrôle disponible est «point» (ou «.»). Il demande à tar d'imprimer un seul point sur le flux de liste standard, par exemple:

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

Le même effet peut être obtenu par:

$ tar -c --checkpoint=.1000 /var
Wojtek
la source
+1 pour l'approche la plus simple! Si vous ne voyez aucun point imprimé, essayez de diminuer le nombre, par exemple --checkpoint=.10. Cela fonctionne également très bien lors de l'extraction avec tar -xz.
Noam Manos
13

Une méthode plus simple qui fonctionne sur mon système à l'aide de l'utilitaire pipeview (pv).

srcdir=$1
outfile=$2


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

Je n'ai rien vu de similaire et toutes les fonctions personnalisées semblent se concentrer uniquement sur le rendu, alors ... ma solution très simple conforme POSIX ci-dessous avec des explications étape par étape car cette question n'est pas triviale.

TL; DR

Le rendu de la barre de progression est très simple. L'estimation de la quantité à rendre doit être différente. Voici comment rendre (animer) la barre de progression - vous pouvez copier et coller cet exemple dans un fichier et l'exécuter:

#!/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} - valeurs de 1 à 20
  • echo -n - imprimer sans nouvelle ligne à la fin
  • echo -e - interpréter des caractères spéciaux lors de l'impression
  • "\r" - retour chariot, un caractère spécial pour revenir au début de la ligne

Vous pouvez lui faire rendre n'importe quel contenu à n'importe quelle vitesse, donc cette méthode est très universelle, par exemple souvent utilisée pour la visualisation de "piratage" dans des films stupides, sans blague.

Réponse complète

Le gros du problème est de savoir comment déterminer la $ivaleur, c'est-à-dire quelle proportion de la barre de progression afficher. Dans l'exemple ci-dessus, je l'ai simplement laissé incrémenter en forboucle pour illustrer le principe, mais une application réelle utiliserait une boucle infinie et calculerait la $ivariable à chaque itération. Pour effectuer ce calcul, il a besoin des ingrédients suivants:

  1. combien de travail il reste à faire
  2. combien de travail a été fait jusqu'à présent

Dans le cas où cpil a besoin de la taille d'un fichier source et de la taille du fichier cible:

#!/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 - vérifier les statistiques des fichiers
  • -c - retourne une valeur formatée
  • %s - taille totale

Dans le cas d'opérations comme le décompression de fichiers, le calcul de la taille de la source est légèrement plus difficile mais toujours aussi simple que d'obtenir la taille d'un fichier non compressé:

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
  • gzip -l - afficher des informations sur l'archive zip
  • tail -n1 - travailler avec 1 ligne à partir du bas
  • tr -s ' ' - traduire plusieurs espaces en un (les presser)
  • cut -d' ' -f3 - couper la 3e colonne délimitée par des espaces

Voici la chair du problème, cependant. Cette solution est de moins en moins générale. Tous les calculs de la progression réelle sont étroitement liés au domaine que vous essayez de visualiser, est-ce une opération sur un seul fichier, un compte à rebours, un nombre croissant de fichiers dans un répertoire, une opération sur plusieurs fichiers, etc., par conséquent, il ne peut pas être réutilisé. La seule partie réutilisable est le rendu de la barre de progression. Pour le réutiliser, vous devez l'abstraire et l'enregistrer dans un fichier (par exemple /usr/lib/progress_bar.sh), puis définir des fonctions qui calculent les valeurs d'entrée spécifiques à votre domaine. Voici à quoi pourrait ressembler un code généralisé (j'ai aussi fait la $BARdynamique parce que les gens le demandaient, le reste devrait être clair maintenant):

#!/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 - un intégré pour imprimer des trucs dans un format donné
  • printf '%50s' - n'imprimez rien, remplissez-le de 50 espaces
  • tr ' ' '#' - traduire chaque espace en signe de hachage

Et voici comment vous l'utiliseriez:

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

Évidemment, il peut être enveloppé dans une fonction, réécrit pour fonctionner avec des flux canalisés, réécrit dans une autre langue, quel que soit votre poison.

cprn
la source
1
Pour ceux qui veulent le truc le plus simple, je viens de faire le mien avec cprn première réponse. C'est une barre de progression très simple dans une fonction qui utilise une règle de proportionnalité stupide pour dessiner la barre: pastebin.com/9imhRLYX
YCN-
9

Barre de progression de style APT (ne casse pas la sortie normale)

entrez la description de l'image ici

EDIT: Pour une version mise à jour, consultez ma page github

Je n'étais pas satisfait des réponses à cette question. Ce que je cherchais personnellement, c'était une barre de progression sophistiquée, comme le voit APT.

J'ai regardé le code source C pour APT et j'ai décidé d'écrire mon propre équivalent pour bash.

Cette barre de progression restera bien en bas du terminal et n'interférera pas avec les sorties envoyées au terminal.

Veuillez noter que la barre est actuellement fixée à 100 caractères de large. Si vous voulez l'adapter à la taille du terminal, cela est également assez facile à réaliser (la version mise à jour sur ma page github gère bien cela).

Je posterai mon script ici. Exemple d'utilisation:

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

Le script (je recommande fortement la version sur mon github à la place):

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

Cela vous permet de visualiser qu'une commande est toujours en cours d'exécution:

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

Cela va créer une boucle while infinie qui s'exécute en arrière-plan et fait écho à un "." chaque seconde. Cela s'affichera .dans le shell. Exécutez la tarcommande ou n'importe quelle commande de votre choix. Lorsque cette commande a terminé son exécution, supprimez le dernier travail exécuté en arrière-plan - qui est la boucle while infinie .

romeror
la source
Un autre travail ne pourrait-il pas démarrer en arrière-plan pendant l'exécution et potentiellement être tué au lieu de la boucle de progression?
Centimane
Je pense que l'idée est que vous mettriez cela dans un script, donc cela ne ferait que piéger une sortie de ce script.
Iguananaut
1
J'adore cette commande, je l'utilise dans mes fichiers. Je suis juste un peu mal à l'aise car je ne comprends pas vraiment comment cela fonctionne. Les première et troisième lignes sont plus faciles à comprendre, mais je ne suis toujours pas sûr. Je sais que c'est une vieille réponse, mais existe-t-il un moyen d'obtenir une explication différente destinée aux débutants en programmation
Felipe
1
C'est la SEULE vraie réponse, où d'autres ne sont que des barres de progression de Scripting 101 qui ne signifient rien et ne sont pas utiles pour de vrais programmes uniques, non traçables (presque TOUS). Je vous remercie.
bekce
@Felipe, La boucle while est un processus d'arrière-plan. Le $! dans le premier piège capture l'ID de processus de ce processus d'arrière-plan et garantit que si le processus actuel / parent se termine, le processus d'arrière-plan meurt également et n'est pas laissé en suspens. L'instruction kill termine simplement le processus d'arrière-plan lorsque votre ou vos commandes longues se terminent.
floydn
7

Voici à quoi cela pourrait ressembler

Téléchargement d'un fichier

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

En attente d'un travail pour terminer

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

Fonction simple qui l'implémente

Vous pouvez simplement le copier-coller dans votre script. Il ne nécessite rien d'autre pour fonctionner.

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

Exemple d'utilisation

Ici, nous téléchargeons un fichier et redessinons la barre de progression à chaque itération. Peu importe le travail réellement effectué tant que nous pouvons obtenir 2 valeurs: la valeur maximale et la valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est file_sizeet la valeur actuelle est fournie par une fonction et est appelée 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"
Vagiz Duseev
la source
Fonction soignée et simple. Merci beaucoup!
Andreas Kraft
C'est ce que je recherche! Merci beaucoup :)
wajdi_jurry
4

La plupart des commandes Unix ne vous donneront pas le type de rétroaction directe à partir de laquelle vous pouvez le faire. Certains vous donneront une sortie sur stdout ou stderr que vous pouvez utiliser.

Pour quelque chose comme tar, vous pouvez utiliser le commutateur -v et diriger la sortie vers un programme qui met à jour une petite animation pour chaque ligne qu'il lit. Au fur et à mesure que tar écrit une liste de fichiers, le programme peut mettre à jour l'animation. Pour terminer un pourcentage, vous devez connaître le nombre de fichiers et compter les lignes.

cp ne donne pas ce genre de sortie pour autant que je sache. Pour surveiller la progression de cp, vous devez surveiller les fichiers source et de destination et surveiller la taille de la destination. Vous pouvez écrire un petit programme c en utilisant l' appel système stat (2) pour obtenir la taille du fichier. Cela permettrait de lire la taille de la source, puis d'interroger le fichier de destination et de mettre à jour une barre% complète en fonction de la taille du fichier écrit à ce jour.

ConcernedOfTunbridgeWells
la source
4

Ma solution affiche le pourcentage de l'archive tar qui est actuellement non compressé et écrit. Je l'utilise lors de l'écriture d'images de système de fichiers racine de 2 Go. Vous avez vraiment besoin d'une barre de progression pour ces choses. Ce que je fais c'est utiliser gzip --list pour obtenir la taille totale non compressée de l'archive tar. À partir de cela, je calcule le facteur de blocage nécessaire pour diviser le fichier en 100 parties. Enfin, j'imprime un message de point de contrôle pour chaque bloc. Pour un fichier de 2 Go, cela donne environ 10 Mo de bloc. Si c'est trop grand, vous pouvez diviser le BLOCKING_FACTOR par 10 ou 100, mais il est alors plus difficile d'imprimer une jolie sortie en termes de pourcentage.

En supposant que vous utilisez Bash, vous pouvez utiliser la fonction shell suivante

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}
}
Noah Spurrier
la source
Belle solution mais comment faire quand on veut compresser un répertoire?
Samir Sadek
4

Tout d'abord, la barre n'est pas le seul indicateur de progression de tuyau. L'autre (peut-être encore plus connu) est le pv (pipe viewer).

Deuxièmement, bar et pv peuvent être utilisés par exemple comme ceci:

$ bar file1 | wc -l 
$ pv file1 | wc -l

ou même:

$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l

une astuce utile si vous voulez utiliser bar et pv dans des commandes qui fonctionnent avec des fichiers donnés en arguments, comme par exemple copier file1 file2, est d'utiliser la substitution de processus :

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

La substitution de processus est une chose magique qui crée des fichiers de tuyaux fifo temporaires / dev / fd / et connecte stdout à partir du processus exécuté (entre parenthèses) via ce tuyau et la copie le voit comme un fichier ordinaire (à une exception près, il ne peut le lire que vers l'avant).

Mise à jour:

La commande bar elle-même permet également la copie. Après la barre d'homme:

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

Mais la substitution de processus est à mon avis une manière plus générique de le faire. Un il utilise le programme cp lui-même.

thedk
la source
3

Je préfère utiliser le dialogue avec le paramètre --gauge . Est utilisé très souvent dans les installations de packages .deb et d'autres éléments de configuration de base de nombreuses distributions. Vous n'avez donc pas besoin de réinventer la roue ... encore

Mettez simplement une valeur int de 1 à 100 @stdin. Un exemple basique et idiot:

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

J'ai ce fichier / bin / Wait (avec chmod u + x perms) à des fins de cuisine: 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"

Je peux donc mettre:

Wait "34 min" "warm up the oven"

ou

Wait "dec 31" "happy new year"

Juan Eduardo Castaño Nestares
la source
2

pour moi le plus facile à utiliser et le plus beau à ce jour est la commande pvoubar comme un gars a déjà écrit

par exemple: besoin de faire une sauvegarde de tout le lecteur avec dd

normalement vous utilisez dd if="$input_drive_path" of="$output_file_path"

avec pvvous pouvez le faire comme ceci:

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

et les progrès vont directement à STDOUTceci:

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

une fois le résumé terminé,

    15654912+0 records in
    15654912+0 records out
    8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
lukassos
la source
Pouvez-vous utiliser pvou barpour visualiser la progression de différents processus, par exemple le compte à rebours du minuteur, la position dans un fichier texte, l'installation de votre application, la configuration de l'exécution, etc.?
cprn
2

De nombreuses réponses décrivent l'écriture de vos propres commandes pour l'impression '\r' + $some_sort_of_progress_msg. Le problème est parfois que l'impression de centaines de ces mises à jour par seconde ralentira le processus.

Cependant, si l'un de vos processus produit une sortie (par exemple, 7z a -r newZipFile myFolderil affichera chaque nom de fichier au fur et à mesure qu'il le compresse), une solution plus simple, rapide, indolore et personnalisable existe.

Installez le module python 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

Aide: tqdm -h. Un exemple utilisant plus d'options:

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

En prime, vous pouvez également utiliser tqdm pour envelopper les itérables dans du code python.

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

casper.dcl
la source
Je ne pense pas que votre exemple avec "plus d'options" fonctionne. Il semble passer le tqdmSTDOUT à wc -ltravers un tuyau. Vous voulez probablement y échapper.
cprn
1
@cprn tqdmaffichera la progression STDERRtout en redirigeant son entrée STDINvers STDOUT. Dans ce cas, wc -ldevrait recevoir la même entrée que si elle tqdmn'était pas incluse.
casper.dcl
Ah, ça a du sens maintenant. Merci d'avoir expliqué.
cprn
2

Sur la base du travail d'Edouard Lopez, j'ai créé une barre de progression qui s'adapte à la taille de l'écran, quelle qu'elle soit. Vérifiez-le.

entrez la description de l'image ici

Il est également publié sur 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

Prendre plaisir

Adriano_Pinaffo
la source
1

Cela ne s'applique qu'à l'aide de gnome zenity. Zenity fournit une excellente interface native pour les scripts bash: https://help.gnome.org/users/zenity/stable/

Exemple de barre de progression 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
tPSU
la source
1

J'ai utilisé une réponse de Création d'une chaîne de caractères répétés dans le script shell pour la répétition de caractères. J'ai deux versions bash relativement petites pour les scripts qui doivent afficher la barre de progression (par exemple, une boucle qui traverse de nombreux fichiers, mais pas utile pour les gros fichiers tar ou les opérations de copie). La plus rapide se compose de deux fonctions, l'une pour préparer les chaînes pour l'affichage des barres:

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

et une pour afficher une barre de progression:

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
}

Il pourrait être utilisé comme:

preparebar 50 "#"

ce qui signifie préparer des chaînes pour la barre avec 50 caractères "#", et après cela:

progressbar 35 80

affichera le nombre de caractères "#" qui correspond au rapport 35/80:

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

N'oubliez pas que la fonction affiche la barre sur la même ligne encore et encore jusqu'à ce que vous (ou un autre programme) imprimiez une nouvelle ligne. Si vous mettez -1 comme premier paramètre, la barre sera effacée:

progressbar -1 80

La version plus lente est tout en une fonction:

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
}

et il peut être utilisé comme (le même exemple que ci-dessus):

progressbar 35 80 50

Si vous avez besoin de la barre de progression sur stderr, ajoutez simplement >&2à la fin de chaque commande printf.

Zarko Zivanov
la source
1

Pour indiquer la progression de l'activité, essayez les commandes suivantes:

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;

OU

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;

OU

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;

OU

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;

On peut utiliser des drapeaux / variables à l'intérieur de la boucle while pour vérifier et afficher la valeur / l'étendue de la progression.

S471
la source
1

En utilisant les suggestions listées ci-dessus, j'ai décidé d'implémenter ma propre barre de progression.

#!/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 "$@"
Qub3r
la source
1
Agréable! pour le faire fonctionner, j'ai dû changer la ligne percent_none="$(( 100 - "$percent_done" ))"pourpercent_none="$(( 100 - $percent_done))"
sergio
0

J'ai fait une version pure shell pour un système embarqué en profitant de:

  • / usr / bin / dd la fonction de gestion du signal SIGUSR1.

    Fondamentalement, si vous envoyez un 'kill SIGUSR1 $ (pid_of_running_dd_process)', il affichera un résumé de la vitesse de débit et du montant transféré.

  • en arrière-plan dd puis en l'interrogeant régulièrement pour les mises à jour, et en générant des ticks de hachage comme les clients ftp de la vieille école le faisaient auparavant.

  • Utilisation de / dev / stdout comme destination pour des programmes non compatibles avec stdout comme scp

Le résultat final vous permet d'effectuer n'importe quelle opération de transfert de fichier et d'obtenir une mise à jour de progression qui ressemble à une sortie FTP de hachage à l'ancienne où vous obtiendrez simplement une marque de hachage pour chaque X octets.

Ce n'est pas du code de qualité de production, mais vous avez l'idée. Je pense que c'est mignon.

Pour ce que ça vaut, le nombre d'octets réel peut ne pas être reflété correctement dans le nombre de hachages - vous pouvez en avoir un de plus ou moins selon les problèmes d'arrondi. Ne l'utilisez pas dans le cadre d'un script de test, c'est juste un régal pour les yeux. Et, oui, je suis conscient que c'est terriblement inefficace - c'est un script shell et je ne m'en excuse pas.

Exemples avec wget, scp et tftp fournis à la fin. Cela devrait fonctionner avec tout ce qui a émis des données. Assurez-vous d'utiliser / dev / stdout pour les programmes qui ne sont pas compatibles avec stdout.

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

Exemples:

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
synthétiseurpatel
la source
Idée décente, tant que vous avez la taille du fichier à l'avance, vous pouvez fournir une valeur ajoutée que pv de cette façon, mais signaler aveuglément que pidof ddc'est effrayant.
Vous avez tenté d'appeler cela avec '# Ma gestion PID est stupide'
synthesizerpatel
Vous pouvez peut - être capturer à $!partir ddet attendre [[ -e /proc/${DD_PID} ]].
0

Dans le cas où vous devez afficher une barre de progression temporelle (en connaissant à l'avance l'heure d'affichage), vous pouvez utiliser Python comme suit:

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

Ensuite, en supposant que vous avez enregistré le script Python sous progressbar.py, il est possible d'afficher la barre de progression de votre script bash en exécutant la commande suivante:

python progressbar.py 10 50

Il afficherait une barre de progression des 50caractères de taille et "en cours d'exécution" pendant 10quelques secondes.

auino
la source
0

J'ai construit sur la réponse fournie par fearside

Cela se connecte à une base de données Oracle pour récupérer la progression d'une restauration RMAN.

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

}

pourrait créer une fonction qui dessine ceci sur une échelle disons 1-10 pour le nombre de barres:

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
Mike Q
la source
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;

production:

0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %

note: si au lieu de 255 vous mettez 1 vous surveillerez la norme en ... avec 2 la norme en sortie (mais vous devez modifier la source pour définir "tot" à la taille du fichier de sortie projeté)

Zibri
la source