Comment ajouter une barre de progression à un script shell?

lors d'un script dans bash ou tout autre shell dans *NIX, alors que 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.

que recommandez-vous pour ajouter des barres de progression aux scripts shell?

331
demandé sur Jens 2008-10-26 17:32:05

30 réponses

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

écrivez \n quand vous avez terminé pour avancer la ligne.

utiliser echo -ne pour:

  1. non imprimé \n et
  2. pour reconnaître des séquences d'évasion comme \r .

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 ce" echec " si vous commencez par une longue ligne et que vous voulez ensuite écrire une courte ligne: dans ce cas, vous aurez besoin d'écraser la longueur de la longue ligne (par exemple, avec des espaces).

613
répondu Mitch Haile 2018-09-06 01:42:01

vous pourriez également être intéressé par comment faire un spinner :

puis-je faire un spinner à Bash?

bien sûr!

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

chaque fois que la boucle itère, il affiche le caractère suivant dans le sp une ficelle qui s'enroule quand elle atteint la fin. (i est la position de le caractère courant à afficher et ${#sp} est la longueur du sp chaîne.)

le la chaîne \B est remplacée par un caractère "backspace". Alternativement, vous pourriez 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 le fonction suivante au début de chaque itération de 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
52
répondu Daenyth 2016-07-09 15:53:15

Certains postes 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 rendront compte de leur progression. Sur les systèmes Linux, certaines commandes répondront de la même manière à SIGUSR1 . Si cette fonction est disponible, vous pouvez utiliser dd pour surveiller le nombre d'octets traités.

alternativement, vous peut utiliser lsof pour obtenir le décalage du pointeur de lecture du fichier, et calculer ainsi la progression. J'ai écrit une commande, nommé pmonitor , qui affiche la progression du traitement d'un processus ou d'un fichier. Avec elle, 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 .

48
répondu Diomidis Spinellis 2015-07-18 15:35:12

utilisez la commande linux pv:

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

il ne sait pas la taille si elle est au milieu du flux, mais il donne une vitesse et total et de là, vous pouvez comprendre combien de temps il devrait prendre et obtenir des commentaires afin que vous savez qu'il n'a pas accroché.

37
répondu Seth Wegner 2011-07-28 17:35:46

a obtenu une fonction de barre de progression facile que j'ai écrit l'autre jour:

#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState() and totalState()
function ProgressBar {
# Process data
    let _progress=(*100/*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 l'accrocher de,

https://github.com/fearside/ProgressBar /

32
répondu fearside 2018-08-14 19:51:12

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

(...) Une autre action de checkpoint disponible est’ dot ‘(ou'.’). Il demande à tar d'imprimer un seul point sur le flot de listes 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
répondu Wojtek 2010-08-02 14:15:23

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

Aperçu

progress-bar.sh in action

Source

Je l'ai mis sur GitHub progress-bar.sh

progress-bar() {
  local duration=


    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
17
répondu Édouard Lopez 2018-07-13 14:01:07

une méthode plus simple qui fonctionne sur mon système en utilisant l'utilitaire pipeview ( pv).

srcdir=
outfile=


tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print }'` | 7za a -si $outfile
12
répondu leebert 2010-07-25 20:07:26

je voudrais aussi contribuer mon propre barre de progrès

il atteint la précision des sous-caractères en utilisant demi-blocs unicode

enter image description here

Le Code

est inclus."

9
répondu nachoparker 2017-07-19 08:12:30

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 un infini tandis que boucle qui exécute en arrière-plan et fait écho à un"."chaque seconde. Cela affichera . dans le shell. Lancez la commande tar ou n'importe quelle commande que vous voulez. Lorsque cette commande termine l'exécution alors kill le dernier travail courant dans l'arrière - plan- qui est le infini alors boucle .

6
répondu romeror 2015-04-19 15:14:36

N'ont rien vu de semblable ainsi... 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 - impression sans nouvelle ligne à la fin
  • echo -e - interpréter des caractères spéciaux lors de l'impression
  • "\r" - retour de chariot, un char 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 Dactylographie. ;)

6
répondu cprn 2017-08-08 18:38:34

Ma solution affiche le pourcentage de l'archive que est actuellement non compressé et écrit. J'utilise cette lors de l'écriture des images du système de fichiers racine de 2 Go. 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 non compressée totale de la tarball. A partir de là je calcule le facteur de blocage nécessaire pour diviser le fichier en 100 parties. Enfin, j'ai l'impression d'un un message de contrôle pour chaque bloc. Pour un fichier de 2Go ceci donne environ 10 Mo par bloc. Si c'est trop grand, alors vous pouvez divisez le BLOCKING_FACTOR par 10 ou 100, mais alors c'est plus difficile à imprimer jolie sortie en termes de pourcentage.

en supposant que vous utilisez Bash alors vous pouvez utiliser le suivant la fonction shell

untar_progress () 
{ 
  TARBALL=
  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
répondu Noah Spurrier 2014-02-12 20:30:26

tout d'abord bar n'est pas le seul tube progress mètre. L'autre (peut-être même plus connu) est pv (pipe viewer).

Deuxièmement bar et pv peut être utilisé 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 la barre et le pv dans les commandes qui fonctionnent avec des fichiers donnés dans les 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 de bash qui crée des fichiers pipes fifo temporaires / dev/ fd / et connecte stdout à partir du processus exécuté (entre parenthèses) à travers ce tuyau et la copie le voit comme un fichier ordinaire (à une exception près, il ne peut que le lire en avant).

mise à jour:

La commande à barre

permet elle-même la copie. Après que l'homme de barre:

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

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

4
répondu thedk 2014-07-18 06:13:10

la plupart des commandes unix ne vous donneront pas le genre de feedback direct à partir duquel vous pouvez faire cela. Certains vous donneront sortie sur stdout ou stderr que vous pouvez utiliser.

pour quelque chose comme tar vous pourriez utiliser le commutateur-v et pipe la sortie à un programme qui met à jour une petite animation pour chaque ligne qu'il lit. Comme tar écrit une liste de fichiers, le programme peut mettre à jour l'animation. Faire un pour cent complet, vous devez connaître le nombre de fichiers et compter les lignes.

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

3
répondu ConcernedOfTunbridgeWells 2008-10-26 14:42:52

je préfère utiliser dialogue avec la --jauge param. Est utilisé très souvent dans .les installations de paquets deb et d'autres trucs de configuration de base de beaucoup de distros. Si vous n'avez pas besoin de réinventer la roue... à nouveau

vient de mettre une valeur int de 1 à 100 @stdin. Un exemple simple et stupide:

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

j'ai cette /bin/Wait fichier (chmod u+x perms) pour la cuisson objectifs: P

#!/bin/bash
INIT=`/bin/date +%s`
NOW=$INIT
FUTURE=`/bin/date -d "" +%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 : "
    sleep 1s
    PTG=`echo "scale=0;$STEP * 100 / $DIFF"|bc -l`
    echo $PTG| dialog --title "$TITLE" --gauge "$TEXT" 7 72
done

if [ "" == "" ]; then msg="Espera terminada: ";audio="Listo";
else msg=;audio=;fi 

/usr/bin/notify-send --icon=stock_appointment-reminder-excl "$msg"
espeak -v spanish "$audio"

pour que je puisse mettre:

Wait "34 min" "warm up the oven"

ou

Wait "dec 31" "happy new year"

3
répondu Juan Eduardo Castaño Nestares 2016-01-10 19:02:18

pour moi le plus facile à utiliser et le plus beau jusqu'à présent est la commande pv ou bar comme un gars a déjà écrit

par exemple: besoin de faire une sauvegarde du lecteur entier avec dd

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

avec pv vous pouvez 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 qu'il est fait le résumé vient

    15654912+0 records in
    15654912+0 records out
    8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
2
répondu lukassos 2015-01-20 10:42:24

pour indiquer le progrès 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 drapeaux/variables à l'intérieur de la boucle while pour vérifier et afficher la valeur/étendue de la progression.

2
répondu Sundeep Borra 2016-03-02 09:12:21

beaucoup de 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 myFolder produira chaque nom de fichier comme il compresse) alors 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 bonus, vous pouvez également utiliser tqdm pour envelopper des itérables en code python.

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

2
répondu abcdaa 2016-04-08 07:21:21

Basé sur le travail d'Edouard Lopez, j'ai créé une barre de progression qui s'adapte à la taille de l'écran, quel qu'il soit. Check it out.

enter image description here

c'est aussi posté 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: "151900920" [SECONDS]"
  case  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
[[  =~ ^[0-9]+$ ]] || error 2

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

Profiter

2
répondu Adriano_epifas 2017-04-29 06:05:36

en utilisant les suggestions énumérées ci-dessus, j'ai décidé de mettre en œuvre ma propre barre de progrès.

#!/usr/bin/env bash

main() {
  for (( i = 0; i <= 100; i=$i + 20)); do
    progress_bar "$i"
    sleep 1;
  done
  progress_bar "done"
  exit 0
}

progress_bar() {
  if [ "" == "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 "$@"
2
répondu Qub3r 2018-01-22 20:05:11

ceci ne s'applique qu'en utilisant gnome zenity. Zenity fournit une excellente interface native aux scripts bash: https://help.gnome.org/users/zenity/stable/

Extrait De L'Exemple De 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
1
répondu tPSU 2014-03-17 12:25:41

j'ai utilisé une réponse de pour créer 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 bash pour les scripts qui doivent afficher la barre de progression (par exemple, une boucle qui passe par de nombreux fichiers, mais pas utile pour les gros fichiers tar ou les opérations de copie). Le plus rapide se compose de deux fonctions, l'une pour préparer les cordes pour l'affichage de la barre:

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

et un pour afficher une barre de progression:

progressbar() {
#  - number (-1 for clearing the bar)
#  - max number
    if [  -eq -1 ]; then
        printf "\r  $barspaces\r"
    else
        barch=$((*barlen/))
        barsp=$((barlen-barch))
        printf "\r[%.${barch}s%.${barsp}s]\r" "$barchars" "$barspaces"
    fi
}

il peut être utilisé comme:

preparebar 50 "#"

qui signifie préparer les cordes 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:

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

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

progressbar -1 80

la version plus lente est tout en une fonction:

progressbar() {
#  - number
#  - max number
#  - number of '#' characters
    if [  -eq -1 ]; then
        printf "\r  %*s\r" ""
    else
        i=$((*/))
        j=$((-i))
        printf "\r[%*s" "$i" | tr ' ' '#'
        printf "%*s]\r" "$j"
    fi
}

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

progressbar 35 80 50

si vous avez besoin de progressbar sur stderr, il suffit d'ajouter >&2 à la fin de chaque commande printf.

1
répondu Zarko Zivanov 2017-05-23 12:18:15
#!/bin/bash

function progress_bar() {
    bar=""
    total=10
    [[ -z  ]] && input=0 || input=
    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 cela 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
1
répondu Mike Q 2017-08-14 14:19:52

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

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

    en gros, si vous envoyez un ' kill SIGUSR1 $(pid_of_running_dd_process)', il sortira un résumé de la vitesse et de la somme transférée.

  • arrière-plan dd, puis l'interroger régulièrement pour obtenir des mises à jour, et générer de hachage comme les tiques les clients ftp de la vieille école le faisaient.

  • utiliser / dev/stdout comme destination pour les programmes non-stdout friendly comme scp

le résultat final vous permet de prendre n'importe quelle opération de transfert de fichier et d'obtenir la mise à jour de progrès qui ressemble à la vieille école FTP 'hash' sortie où vous obtiendriez juste un marque de hash pour chaque x bytes.

ce n'est pas un code de qualité de production, mais vous avez l'idée. Je pense elle est mignonne.

pour ce que ça vaut, le décompte réel des octets pourrait ne pas être reflété correctement dans le nombre de hash-vous pouvez en avoir un de plus ou moins selon les questions d'arrondissement. Ne l'utilise pas comme une partie d'un script de test, c'est juste un bonbon. Et, oui, je sais 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. Il devrait fonctionner avec tout ce qui émet des données. Assurez-vous d'utiliser /dev/stdout pour les programmes qui ne sont pas stdout friendly.

#!/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  }' >> ${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  }')

        # 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
0
répondu synthesizerpatel 2014-10-10 01:03:13

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

alors, en supposant que vous ayez enregistré le script Python comme 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 montrerait une barre de progression de taille 50 caractères et" exécution "pour 10 secondes.

0
répondu auino 2015-03-27 09:45:51

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() and totalState()
 function ProgressBar {
 # Process data
let _progress=(*100/*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
répondu CH55 2016-02-10 11:54:02

c'est une barre de progression psychédélique pour bash scripting par nExace. Il peut être appelé à partir de la ligne de commande comme './progressbar x y' où 'x' est le temps en secondes et 'y' est un message associé à la partie de la progression.

la fonction de progressbar interne() elle-même est bonne standalone ainsi si vous voulez d'autres parties de votre script pour contrôler la progressbar. Par exemple, en envoyant 'progressbar 10' Creating directory tree'; 'affichera:

[#######                                     ] (10%) Creating directory tree

bien sûr que ce sera bien psychédélique...

#!/bin/bash

if [ "$#" -eq 0 ]; then echo "x is \"time in seconds\" and z is \"message\""; echo "Usage: progressbar x z"; exit; fi
progressbar() {
        local loca=; local loca2=;
        declare -a bgcolors; declare -a fgcolors;
        for i in {40..46} {100..106}; do
                bgcolors+=("$i")
        done
        for i in {30..36} {90..96}; do
                fgcolors+=("$i")
        done
        local u=$(( 50 - loca ));
        local y; local t;
        local z; z=$(printf '%*s' "$u");
        local w=$(( loca * 2 ));
        local bouncer=".oO°Oo.";
        for ((i=0;i<loca;i++)); do
                t="${bouncer:((i%${#bouncer})):1}"
                bgcolor="\E[${bgcolors[RANDOM % 14]}m \033[m"
                y+="$bgcolor";
        done
        fgcolor="\E[${fgcolors[RANDOM % 14]}m"
        echo -ne " $fgcolor$t$y$z$fgcolor$t \E[96m(\E[36m$w%\E[96m)\E[92m $fgcolor$loca2\033[m\r"
};
timeprogress() {
        local loca=""; local loca2="";
        loca=$(bc -l <<< scale=2\;"$loca/50")
        for i in {1..50}; do
                progressbar "$i" "$loca2";
                sleep "$loca";
        done
        printf "\n"
};
timeprogress "" ""
0
répondu nexace 2016-05-23 00:39:30

d'abord exécuter le processus à l'arrière-plan, puis regarder son état courant fréquemment,qui était en cours d'exécution imprimer le modèle et vérifier à nouveau que l'État était en cours d'exécution ou non;

utilisant while loop pour suivre l'état du processus fréquemment.

utilisez le pgrep ou toute autre commande pour surveiller et obtenir l'état de fonctionnement d'un processus.

si vous utilisez pgrep redirigez la sortie inutile vers /dev/null si nécessaire.

Code:

sleep 12&
while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done

Ce "#" sera imprimé jusqu'à la veille de terminer,cette méthode utilisée pour mettre en œuvre la barre de progression pour le progrès d'un programme.

vous pouvez également utiliser cette méthode pour les commandes de shell scripts pour analyser le temps de traitement comme visuel.

BUG: cette méthode pgrep ne fonctionne pas dans toutes les situations,de manière inattendue l'autre processus était en cours avec même nom, la boucle while ne se termine pas.

donc obtenir l'état de fonctionnement du processus en spécifiant qu'il est PID, en utilisant mai le processus peut disponible avec certaines commandes,

la commande ps liste tous les processus avec l'id,vous avez besoin de grep pour trouver le pid du processus spécifié

0
répondu purushothaman poovai 2016-06-13 10:23:43

j'ai voulu suivre les progrès basés sur le nombre de lignes qu'une commande a produit contre un nombre cible de lignes d'une exécution précédente:

#!/bin/bash
function lines {
  local file=
  local default=
  if [[ -f $file ]]; then
    wc -l $file | awk '{print }';
  else
    echo $default
  fi
}

function bar {
  local items=
  local total=
  local size=
  percent=$(($items*$size/$total % $size))
  left=$(($size-$percent))
  chars=$(local s=$(printf "%${percent}s"); echo "${s// /=}")
  echo -ne "[$chars>";
  printf "%${left}s"
  echo -ne ']\r'
}

function clearbar {
  local size=
  printf " %${size}s  "
  echo -ne "\r"
}

function progress {
  local pid=
  local total=
  local file=

  bar 0 100 50
  while [[ "$(ps a | awk '{print }' | grep $pid)" ]]; do
    bar $(lines $file 0) $total 50
    sleep 1
  done
  clearbar 50
  wait $pid
  return $?
}

alors utilisez - le comme ceci:

target=$(lines build.log 1000)
(mvn clean install > build.log 2>&1) &
progress $! $target build.log

il produit une barre de progrès qui ressemble à quelque chose comme ceci:

[===============================================>   ]

la barre augmente lorsque le nombre de lignes atteint la cible. Si le nombre de lignes dépasse la cible, la barre recommence (avec un peu de chance la cible est bonne).

BTW: j'utilise bash sur Mac OSX. J'ai basé ce code sur un spinner de mariascio .

0
répondu pbatey 2016-07-09 00:04:53

voici à quoi ça pourrait ressembler

télécharger un fichier

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

en Attente d'un travail à terminer

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

Simple fonction qui la met en œuvre

vous pouvez simplement copier-coller à votre script. Il n'exige rien d'autre pour travailler.

PROGRESS_BAR_WIDTH=50  # progress bar length in characters

draw_progress_bar() {
  # Arguments: current value, max value, unit of measurement (optional)
  local __value=
  local __max=
  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échargez un fichier et redessinez la barre de progression à chaque itération. Peu importe quel travail est réellement effectué tant que nous pouvons obtenir 2 valeurs: la valeur max et la valeur actuelle.

dans l'exemple au-dessous de la valeur max est file_size et la valeur actuelle est fournie par une certaine 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"
0
répondu Vagiz Duseev 2018-09-30 20:24:12