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?
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:
- non imprimé
\n
et - 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).
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
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 .
utilisez la commande linux 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é.
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 /
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
je cherchais quelque chose de plus sexy que la réponse choisie, ainsi que mon propre script.
Aperçu
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
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
je voudrais aussi contribuer mon propre barre de progrès
il atteint la précision des sous-caractères en utilisant demi-blocs unicode
Le Codeest inclus."
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 .
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. ;)
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}
}
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 à barrepermet 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.
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.
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"
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
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.
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.
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.
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
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 "$@"
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
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.
#!/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
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
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.
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'
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 "" ""
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é
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 .
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"