Question

Une barre de progression est nécessaire lors de la rédaction de scripts sous bash ou de tout autre shell sous * NIX, 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?

Était-ce utile?

La solution

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.

Écrivez \ n lorsque vous avez terminé d'avancer la ligne.

Utilisez echo -ne pour:

  1. pas imprimer \ n et
  2. pour reconnaître les séquences d'échappement telles que \ 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 cette erreur "échoue". si vous commencez par une longue ligne et souhaitez ensuite écrire une ligne courte: dans ce cas, vous devrez remplacer la longueur de la ligne longue (par exemple, par des espaces).

Autres conseils

Vous pouvez également être intéressé par la comment faire un spinner :

Puis-je faire une spinner dans Bash?

  

Bien sûr!

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

A chaque itération de la boucle, le prochain caractère de la boucle est affiché.   chaîne, enrouler comme il arrive à la fin. (i est la position de   le caractère actuel à afficher et $ {# sp} est la longueur du sp   chaîne).

     

La chaîne \ b est remplacée par un caractère 'retour arrière'. Alternativement   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 l’impression).

     

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

Certains articles 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épondent 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 de le pointeur de lecture du fichier, et calcule 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 lui, vous pouvez faire des choses telles que:

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

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

utilisez la commande linux pv:

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

il ne connaît pas la taille si elle se trouve au milieu du flux, mais il 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 le problème ne s'est pas figé.

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-

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

<*>

Ou s'en prendre à,
https://github.com/fearside/ProgressBar/

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/

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

Aperçu

 progress-bar.sh en action

Source

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

Utilisation

 progress-bar 100

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

(...) Une autre action de point de contrôle disponible est & # 8216; dot & # 8217; (ou & # 8216;. & # 8217;). Il indique à 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

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

Je souhaite également contribuer à mon progrès réalisés barre

Il permet d'obtenir une précision des sous-caractères en utilisant les blocs Half Unicode

.

 entrez la description de l'image ici

Le code est inclus

Ceci 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

Ceci créera une boucle while infinie qui s'exécutera en arrière-plan et fera écho à un "." chaque seconde. Ceci affichera . dans le shell. Exécutez la commande tar ou une commande de votre choix. Lorsque cette commande a fini de s'exécuter, kill le dernier travail exécuté en arrière-plan - la boucle infinie while .

Je n'ai 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 - affiche sans nouvelle ligne à la fin
  • echo -e - interprète les caractères spéciaux lors de l'impression
  • " \ r " - return, un caractère spécial pour revenir au début de la ligne

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

La plupart des commandes unix ne vous donneront pas le type de retour direct à partir duquel vous pouvez le faire. Certains vous donneront une sortie que vous pouvez utiliser sur stdout ou stderr.

Dans le cas de 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 le nombre de 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 pouvez écrire un petit programme en utilisant l’appel système stat (2) pour obtenir le taille du fichier. Cela lirait la taille de la source, puis interrogerait le fichier de destination et mettrait à jour une barre% de complétion en fonction de la taille du fichier écrit à ce jour.

Ma solution affiche le pourcentage de l'archive tar qui est actuellement décompressé et écrit. Je l'utilise 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 c'est utiliser gzip --list pour obtenir la taille totale non compressée du tarball. A partir de cela, je calcule le facteur de blocage nécessaire 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 10Mo un bloc. Si c'est trop gros, alors vous pouvez diviser le BLOCKING_FACTOR par 10 ou 100, mais alors c'est plus difficile d’imprimer de jolis résultats en termes de pourcentage.

En supposant que vous utilisez Bash, vous pouvez utiliser le fonction de 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}
}

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

Une astuce utile si vous souhaitez utiliser les commandes bar et pv dans des fichiers utilisant des arguments, comme par exemple. copier fichier1 fichier2 doit utiliser le 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 tuyau fifo temporaires / dev / fd / et connecte stdout à partir du processus exécuté (entre parenthèses) via ce tuyau et que la copie le voit comme un fichier ordinaire (à une exception lisez-le seulement).

Mise à jour:

La commande

barre elle-même permet également la copie. Bar après l'homme:

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

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

Je préfère utiliser dialog avec le paramètre - gauge . 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 ... encore une fois

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) à des fins de 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"

Je peux donc mettre:

Attendre " 34 min " "réchauffer le four"

ou

Attendez & dec; dec 31 " "Bonne année"

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige 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 (( 

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

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

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_max < 1 )); then __max=1; fi # anti zero division protection local __percentage=$(( 100 - (

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_max*100 -

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_value*100) /

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_max )) # Rescale the bar according to the progress bar width local __num_bar=$((

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_percentage * $PROGRESS_BAR_WIDTH / 100 )) # Draw progress bar printf "[" for b in $(seq 1

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_num_bar); do printf "#"; done for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH -

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_num_bar ))); do printf " "; done printf "]

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_percentage%% (

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_value /

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_max

Voici à quoi cela pourrait ressembler

Chargement d'un fichier

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

En attente de la fin d'un travail

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

Fonction simple qui l'implémente

Vous pouvez simplement copier-coller dans votre script. Il n’exige rien d’autre pour fonctionner.

<*>

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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>_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: valeur maximale et valeur actuelle.

Dans l'exemple ci-dessous, la valeur maximale est taille_fichier et la valeur actuelle est fournie par une fonction et s'appelle upload_bytes .

<*>

Barre de progression du style APT (Ne pas interrompre la sortie normale)

 entrer la description de l'image ici

MODIFIER: pour obtenir 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 constate APT.

J’ai jeté un œil au code source C pour APT et décidé d’écrire mon propre équivalent pour bash.

Cette barre de progression reste 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 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}"
}

pour moi, la commande pv ou bar est la plus facile à utiliser et la plus esthétique à ce jour, comme un type l'a déjà écrit

.

par exemple: nécessité de sauvegarder le lecteur entier 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 la progression va directement à STDOUT comme ceci:

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

après avoir terminé, le résumé s'affiche

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

De nombreuses réponses décrivent l’écriture de vos propres commandes d’impression de '\ 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 et -r newZipFile monFichier générera chaque nom de fichier au fur et à mesure qu'il le compresse), une solution plus simple, rapide, sans douleur 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 éléments itérables dans du code python.

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

Ceci est uniquement applicable avec gnome zenity. Zenity fournit une excellente interface native pour les scripts bash: https://help.gnome.org/users/zenity/stable/

Exemple dans la barre de progression de 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

J'ai utilisé une réponse de Création d'une chaîne de caractères répétés dans un script shell pour répéter le caractère. J'ai deux versions bash relativement petites 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 en vue de l’affichage des barres:

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

qui signifie préparer des chaînes pour bar 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 la plus lente ne contient qu'une seule 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 (même exemple que ci-dessus):

progressbar 35 80 50

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

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 drapeaux / variables dans la boucle while pour vérifier et afficher la valeur / l’étendue de la progression.

Sur la base du travail d’Edouard Lopez, j’ai créé une barre de progression qui correspond à la taille de l’écran, quelle qu’elle soit. Découvrez-le.

 entrer la description de l'image ici

Il est également publié sur le 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: <*> [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

Profitez

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 "$@"

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.

    En gros, si vous envoyez un 'kill SIGUSR1 $ (pid_of_running_dd_process)', il sera affiché un résumé de la vitesse de traitement et de la quantité transférée.

  • mettre en arrière-plan dd puis l'interroger régulièrement pour obtenir des mises à jour et générer les tiques comme les clients ftp de la vieille é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 'hash' 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 (nar+progress-script@remix.net) 2010 
# LGPLv2 license
# If you use this, send me an email to say thanks and let me know what your product
# is so I can tell all my friends I'm a big man on the internet!

progress_filter() {

        local START=$(date +"%s")
        local SIZE=1
        local DURATION=1
        local BLKSZ=51200
        local TMPFILE=/tmp/tmpfile
        local PROGRESS=/tmp/tftp.progress
        local BYTES_LAST_CYCLE=0
        local BYTES_THIS_CYCLE=0

        rm -f ${PROGRESS}

        dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
                | grep --line-buffered -E '[[:digit:]]* bytes' \
                | awk '{ print $1 }' >> ${PROGRESS} &

        # Loop while the 'dd' exists. It would be 'more better' if we
        # actually looked for the specific child ID of the running 
        # process by identifying which child process it was. If someone
        # else is running dd, it will mess things up.

        # My PID handling is dumb, it assumes you only have one running dd on
        # the system, this should be fixed to just get the PID of the child
        # process from the shell.

        while [ $(pidof dd) -gt 1 ]; do

                # PROTIP: You can sleep partial seconds (at least on linux)
                sleep .5    

                # Force dd to update us on it's progress (which gets
                # redirected to $PROGRESS file.
                # 
                # dumb pid handling again
                pkill -USR1 dd

                local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
                local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))

                # Don't print anything unless we've got 1 block or more.
                # This allows for stdin/stderr interactions to occur
                # without printing a hash erroneously.

                # Also makes it possible for you to background 'scp',
                # but still use the /dev/stdout trick _even_ if scp
                # (inevitably) asks for a password. 
                #
                # Fancy!

                if [ $XFER_BLKS -gt 0 ]; then
                        printf "#%0.s" $(seq 0 $XFER_BLKS)
                        BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
                fi
        done

        local SIZE=$(stat -c"%s" $TMPFILE)
        local NOW=$(date +"%s")

        if [ $NOW -eq 0 ]; then
                NOW=1
        fi

        local DURATION=$(($NOW-$START))
        local BYTES_PER_SECOND=$(( SIZE / DURATION ))
        local KBPS=$((SIZE/DURATION/1024))
        local MD5=$(md5sum $TMPFILE | awk '{ print $1 }')

        # This function prints out ugly stuff suitable for eval() 
        # rather than a pretty string. This makes it a bit more 
        # flexible if you have a custom format (or dare I say, locale?)

        printf "\nDURATION=%d\nBYTES=%d\nKBPS=%f\nMD5=%s\n" \
            $DURATION \
            $SIZE \
            $KBPS \
            $MD5
}

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

Si 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 en tant que 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 de la taille de 50 caractères et "en cours d'exécution". pendant 10 secondes.

J'ai bâti 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-

J'ai bâti 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.

<*>done # Build progressbar string lengths _fill=$(printf "%${_done}s") _empty=$(printf "%${_left}s") # 1.2 Build progressbar strings and print the ProgressBar line # 1.2.1 Output example: # 1.2.1.1 Progress : [########################################] 100% printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%" } function rman_check { sqlplus -s / as sysdba <<EOF set heading off set feedback off select round((sofar/totalwork) * 100,0) pct_done from v\$session_longops where totalwork > sofar AND opname NOT LIKE '%aggregate%' AND opname like 'RMAN%'; exit EOF } # Variables _start=1 # This accounts as the "totalState" variable for the ProgressBar function _end=100 _rman_progress=$(rman_check) #echo ${_rman_progress} # Proof of concept #for number in $(seq ${_start} ${_end}) while [ ${_rman_progress} -lt 100 ] do for number in _rman_progress do sleep 10 ProgressBar ${number} ${_end} done _rman_progress=$(rman_check) done printf '\nFinished!\n'
#!/bin/bash

function progress_bar() {
    bar=""
    total=10
    [[ -z $1 ]] && input=0 || input=${1}
    x="##"
   for i in `seq 1 10`; do
        if [ $i -le $input ] ;then
            bar=$bar$x
        else
            bar="$bar  "
       fi
    done
    #pct=$((200*$input/$total % 2 + 100*$input/$total))
    pct=$(($input*10))
    echo -ne "Progress : [ ${bar} ] (${pct}%) \r"    
    sleep 1
    if [ $input -eq 10 ] ;then
        echo -ne '\n'
    fi

}

pourrait créer une fonction qui dessine ceci sur une échelle, par exemple 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

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

Création de 40% de progression: programme 40

 entrer la description de l'image ici

#!/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 vous mettez 1 au lieu de 1, vous surveillerez la norme dans ... avec 2 la sortie standard (mais vous devez modifier la source pour définir "tot" sur la taille de fichier de sortie projetée)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top