web-dev-qa-db-fra.com

Comment ajouter une barre de progression à un script shell?

Lors de la création de scripts dans bash ou dans un autre shell dans * NIX, une barre de progression est nécessaire lors de l'exécution d'une commande prenant plus de quelques secondes.

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?

351
Tom Feiner

Vous pouvez implémenter ceci en écrasant une ligne. Utilisez \r pour revenir au début de la ligne sans écrire \n sur le terminal.

Ecrivez \n lorsque vous avez terminé pour faire avancer la ligne.

Utilisez echo -ne pour:

  1. pas imprimer \n et
  2. 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 cet "échec" si vous commencez par une longue ligne et souhaitez ensuite écrire une courte ligne: dans ce cas, vous devrez remplacer la longueur de la longue ligne (par exemple, par des espaces).

639
Mitch Haile

Vous pourriez aussi être intéressé par comment faire un spinner :

Puis-je faire une 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 sp chaîne, enrouler comme il arrive à la fin. (i est la position de le caractère actuel à afficher et $ {# sp} est la longueur de la chaîne sp ).

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

Si vous souhaitez ralentir, insérez une commande de veille dans 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 le fonction suivante au début de chaque itération pour mettre à jour le fichier fileur:

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
57
Daenyth

Certains messages ont montré comment afficher la progression de la commande. Pour le calculer, vous aurez besoin de voir combien vous avez progressé. Sur les systèmes BSD, certaines commandes, telles que dd (1), acceptent un signal SIGINFO et rendent compte de leur progression. Sur les systèmes Linux, certaines commandes répondront de la même manière que SIGUSR1. Si cette fonction est disponible, vous pouvez diriger votre entrée via dd pour surveiller le nombre d'octets traités.

Vous pouvez également utiliser lsof pour obtenir le décalage du pointeur de lecture du fichier et calculer ainsi 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 cela, vous pouvez faire des choses, telles que les suivantes.

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

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

48
Diomidis Spinellis

utilisez la commande linv pv:

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

elle ne connaît pas la taille si elle se trouve au milieu du flux, mais elle donne une vitesse et un total et à partir de là, vous pouvez déterminer le temps que cela prend et obtenir un retour afin que vous sachiez que rien ne s'interrompt.

38
Seth Wegner

Vous avez 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 s'en prendre à,
https://github.com/fearside/ProgressBar/

34
fearside

Je recherchais quelque chose de plus sexy que la réponse choisie, ainsi que mon propre script.

Aperçu

 progress-bar.sh in action

La source

Je le mets sur 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
}

Usage

 progress-bar 100
22
Édouard Lopez

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

(...) Une autre action disponible au point de contrôle est «point» (ou «.»). Il indique à tar d'imprimer un seul point sur le flux de référencement standard, par exemple:

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

Le même effet peut être obtenu par:

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

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
11
leebert

Je souhaite également contribuer à ma propre barre de progression

Il atteint la précision des sous-caractères en utilisant Demi-blocs unicode

 enter image description here

Le code est inclus

10
nachoparker

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 créera une boucle infinite while qui s’exécute en arrière-plan et renvoie un "." chaque seconde. Ceci affichera . dans le shell. Exécutez la commande tar ou toute autre commande de votre choix. Lorsque cette commande a fini d'exécuter, puis kill le dernier travail exécuté en arrière-plan - qui est le infinite while loop.

6
romeror

N'ayant rien vu de semblable alors ... ma solution très simple:

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

Je l'ai utilisé il y a longtemps dans une simple "vidéo de piratage" pour simuler le code de frappe. ;)

5
cprn

Tout d'abord, la barre n'est pas le seul indicateur de progression de tuyau. L'autre (peut-être même plus connu) est 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

un truc utile si vous voulez utiliser les commandes bar et pv dans des commandes qui travaillent avec des fichiers donnés en arguments, comme par exemple. copier fichier1 fichier2, doit utiliser traiter la substitution :

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

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

Mettre à jour:

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

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. Il utilise le programme cp lui-même.

4
thedk

Ma solution affiche le pourcentage de l'archive est en cours de décompression et d'écriture. J'utilise ceci Lors de l'écriture d'images de 2 Go du système de fichiers racine. Vous avez vraiment besoin d'une barre de progression pour ces choses. Ce que je fais est d'utiliser gzip --list pour obtenir la taille totale non compressée de l'archive A partir de cela, je calcule le facteur de blocage nécessairepour 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, thisgive environ 10 Mo par bloc. Si cela est trop important, vous pouvez diviser le BLOCKING_FACTOR par 10 ou 100, mais il est alors plus difficile d'imprimer un résultat en pourcentage.

En supposant que vous utilisiez 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}
}
4
Noah Spurrier

La plupart des commandes unix ne vous donneront pas le type de retour direct à partir duquel vous pouvez le faire .. Certaines vous donneront une sortie que vous pourrez utiliser.

Pour quelque chose comme tar, vous pouvez utiliser le commutateur -v et diriger la sortie vers un programme mettant à jour une petite animation pour chaque ligne lue. Comme tar écrit une liste de fichiers, le programme peut mettre à jour l'animation. Pour compléter 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 cible et surveiller la taille de la destination. Vous pourriez écrire un petit programme en utilisant l'appel système stat (2) pour obtenir la taille du fichier. Cela lirait la taille de la source, interrogerait le fichier de destination et mettrait à jour une barre de pourcentage de complétion en fonction de la taille du fichier écrit à ce jour.

Je préfère utiliser dialog avec le --gauge param. Est très souvent utilisé dans les installations de paquetages .deb et autres configurations de base de nombreuses distributions. Donc, vous n'avez pas besoin de réinventer la roue ... à nouveau

Il suffit de mettre 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) pour la cuisson: 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"

Donc je peux mettre:

Wait "34 min" "warm up the oven"

ou 

Wait "dec 31" "happy new year"

pour moi, plus facile à utiliser et plus esthétique à ce jour est la commande pv ou bar

par exemple: nécessité de faire une sauvegarde de tout le lecteur avec dd

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

avec pv vous pouvez le faire comme ceci: 

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

et le progrès va directement à STDOUT comme ceci:

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

après cela, le résumé arrive

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

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

Cependant, si l'un de vos processus produit une sortie (par exemple, 7z a -r newZipFile myFolder produira chaque nom de fichier au fur et à mesure qu'il le compresse), une solution plus simple, rapide, simple 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 bonus, vous pouvez également utiliser tqdm pour envelopper des iterables dans du code python.

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

2
abcdaa

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

 enter image description here

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

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

J'ai jeté un coup d'œil au 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, c'est assez facile à faire (la version mise à jour sur ma page github gère cela bien).

Je vais poster 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}"
}
1
polle

En utilisant les suggestions énumérées ci-dessus, j'ai décidé de mettre en œuvre 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 "$@"
1
Qub3r

Basé sur le travail d’Edouard Lopez, j’ai créé une barre de progression qui convient à la taille de l’écran, quelle qu’elle soit. Vérifiez-le.

 enter image description here

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

1
Adriano_epifas

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;

Vous pouvez utiliser flags/variables à l'intérieur de la boucle while pour vérifier et afficher la valeur/le degré de progression.

1
Sundeep Borra

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 relativement petites de bash pour les scripts qui doivent afficher une barre de progression (par exemple, une boucle qui parcourt de nombreux fichiers mais qui n'est pas utile pour les gros fichiers tar ou les opérations de copie). Le plus rapide comprend deux fonctions, l’une pour préparer les chaînes à l’affichage:

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

et un 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 barre avec 50 "#" caractères, et après cela:

progressbar 35 80

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

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

Sachez que cette 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 progressbar sur stderr, ajoutez simplement >&2 à la fin de chaque commande printf. 

1
Zarko Zivanov

Ceci n'est applicable qu'avec gnome zenity. Zenity fournit une excellente interface native aux 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
1
tPSU

J'ai construit sur la réponse fournie par le côté peur

Ceci 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'
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

}

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
0
Mike Q

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

Créer 40% de progression: progreSh 40

 enter image description here

0
Hello World

J'ai créé une version purement Shell pour un système embarqué en tirant parti des avantages suivants:

  • La fonction de traitement du signal SIGUSR1 de/usr/bin/dd.

    Fondamentalement, si vous envoyez un message 'kill SIGUSR1 $ (pid_of_running_dd_process)', il générera Un résumé de la vitesse de traitement et du montant transféré. 

  • mettre en arrière-plan dd puis l'interroger régulièrement pour obtenir des mises à jour, et générer des ticks de hachage comme les clients ftp de l'ancienne école.

  • Utilisation de/dev/stdout comme destination pour les programmes non stdout comme scp

Le résultat final vous permet de prendre n’importe quelle opération de transfert de fichier et d’obtenir une mise à jour de progression qui ressemble à une sortie FTP de la vieille école, où vous obtiendrez simplement un signe dièse pour chaque X octets.

Ce n'est pas un code de qualité de production, mais vous voyez l'idée. Je trouve ça mignon 

Pour ce que cela 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 en fonction des problèmes d'arrondi. N'utilisez pas ceci dans le cadre d'un script de test, c'est juste bon à savoir. 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 émet 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 [email protected]:~/myfile.tar /dev/stdout | progress_filter
0
synthesizerpatel

Voici à quoi cela pourrait ressembler

Télécharger 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 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 exécuté tant que nous pouvons obtenir 2 valeurs: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est file_size et la valeur actuelle est fournie par une fonction et s'appelle 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

Si vous devez afficher une barre de progression temporelle (en connaissant à l'avance le temps 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 en tant que progressbar.py, il est possible d'afficher la barre de progression à partir de votre script bash en exécutant la commande suivante:

python progressbar.py 10 50

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

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;

sortie:

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

remarque: si au lieu de 255, vous mettez 1, vous surveillez la norme en entrée ... avec 2 en sortie standard (mais vous devez modifier la source pour définir "tot" sur la taille du fichier de sortie projetée)

0
Zibri