Lors de la création de scripts dans bash ou dans un autre shell dans * NIX, une barre de progression est nécessaire lors de l'exécution d'une commande prenant plus de quelques secondes.
Par exemple, copier un gros fichier, ouvrir un gros fichier tar.
Quelles méthodes recommandez-vous pour ajouter des barres de progression aux scripts Shell?
Vous pouvez implémenter ceci en écrasant une ligne. Utilisez \r
pour revenir au début de la ligne sans écrire \n
sur le terminal.
Ecrivez \n
lorsque vous avez terminé pour faire avancer la ligne.
Utilisez echo -ne
pour:
\n
et\r
.Voici une démo:
echo -ne '##### (33%)\r'
sleep 1
echo -ne '############# (66%)\r'
sleep 1
echo -ne '####################### (100%)\r'
echo -ne '\n'
Dans un commentaire ci-dessous, puk mentionne cet "échec" si vous commencez par une longue ligne et souhaitez ensuite écrire une courte ligne: dans ce cas, vous devrez remplacer la longueur de la longue ligne (par exemple, par des espaces).
Vous pourriez aussi être intéressé par comment faire un spinner :
Sûr!
i=1 sp="/-\|" echo -n ' ' while true do printf "\b${sp:i++%${#sp}:1}" done
Chaque fois que la boucle itère, elle affiche le caractère suivant dans la sp chaîne, enrouler comme il arrive à la fin. (i est la position de le caractère actuel à afficher et $ {# sp} est la longueur de la chaîne sp ).
La chaîne\b est remplacée par un caractère 'retour arrière'. Sinon, vous pouvez jouer avec\r pour revenir au début de la ligne.
Si vous souhaitez ralentir, insérez une commande de veille dans la boucle (après le printf).
Un équivalent POSIX serait:
sp='/-\|' printf ' ' while true; do printf '\b%.1s' "$sp" sp=${sp#?}${sp%???} done
Si vous avez déjà une boucle qui fait beaucoup de travail, vous pouvez appeler le fonction suivante au début de chaque itération pour mettre à jour le fichier fileur:
sp="/-\|" sc=0 spin() { printf "\b${sp:sc++:1}" ((sc==${#sp})) && sc=0 } endspin() { printf "\r%s\n" "$@" } until work_done; do spin some_work ... done endspin
Certains messages ont montré comment afficher la progression de la commande. Pour le calculer, vous aurez besoin de voir combien vous avez progressé. Sur les systèmes BSD, certaines commandes, telles que dd (1), acceptent un signal SIGINFO
et rendent compte de leur progression. Sur les systèmes Linux, certaines commandes répondront de la même manière que SIGUSR1
. Si cette fonction est disponible, vous pouvez diriger votre entrée via dd
pour surveiller le nombre d'octets traités.
Vous pouvez également utiliser lsof
pour obtenir le décalage du pointeur de lecture du fichier et calculer ainsi la progression. J'ai écrit une commande, nommée pmonitor , qui affiche la progression du traitement d'un processus ou d'un fichier spécifié. Avec cela, vous pouvez faire des choses, telles que les suivantes.
$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%
Une version antérieure des scripts Linux et du shell FreeBSD apparaît sur mon blog .
utilisez la commande linv pv:
elle ne connaît pas la taille si elle se trouve au milieu du flux, mais elle donne une vitesse et un total et à partir de là, vous pouvez déterminer le temps que cela prend et obtenir un retour afin que vous sachiez que rien ne s'interrompt.
Vous avez une fonction de barre de progression facile que j'ai écrite l'autre jour:
#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState($1) and totalState($2)
function ProgressBar {
# Process data
let _progress=(${1}*100/${2}*100)/100
let _done=(${_progress}*4)/10
let _left=40-$_done
# Build progressbar string lengths
_fill=$(printf "%${_done}s")
_empty=$(printf "%${_left}s")
# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /\#}${_empty// /-}] ${_progress}%%"
}
# Variables
_start=1
# This accounts as the "totalState" variable for the ProgressBar function
_end=100
# Proof of concept
for number in $(seq ${_start} ${_end})
do
sleep 0.1
ProgressBar ${number} ${_end}
done
printf '\nFinished!\n'
Ou s'en prendre à,
https://github.com/fearside/ProgressBar/
Je recherchais quelque chose de plus sexy que la réponse choisie, ainsi que mon propre script.
Je le mets sur github progress-bar.sh
progress-bar() {
local duration=${1}
already_done() { for ((done=0; done<$elapsed; done++)); do printf "▇"; done }
remaining() { for ((remain=$elapsed; remain<$duration; remain++)); do printf " "; done }
percentage() { printf "| %s%%" $(( (($elapsed)*100)/($duration)*100/100 )); }
clean_line() { printf "\r"; }
for (( elapsed=1; elapsed<=$duration; elapsed++ )); do
already_done; remaining; percentage
sleep 1
clean_line
done
clean_line
}
progress-bar 100
GNU tar a une option utile qui donne les fonctionnalités d'une barre de progression simple.
(...) Une autre action disponible au point de contrôle est «point» (ou «.»). Il indique à tar d'imprimer un seul point sur le flux de référencement standard, par exemple:
$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
Le même effet peut être obtenu par:
$ tar -c --checkpoint=.1000 /var
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 à ma propre barre de progression
Il atteint la précision des sous-caractères en utilisant Demi-blocs unicode
Le code est 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 une boucle infinite while qui s’exécute en arrière-plan et renvoie un "." chaque seconde. Ceci affichera .
dans le shell. Exécutez la commande tar
ou toute autre commande de votre choix. Lorsque cette commande a fini d'exécuter, puis kill le dernier travail exécuté en arrière-plan - qui est le infinite while loop.
N'ayant rien vu de semblable alors ... ma solution très simple:
#!/bin/bash
BAR='####################' # this is full bar, mine is 20 chars
for i in {1..20}; do
echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
sleep .1
done
echo -n
- imprimer sans nouvelle ligne à la finecho -e
- interprète les caractères spéciaux lors de l'impression"\r"
- retour de chariot, un caractère spécial pour revenir au début de la ligneJe l'ai utilisé il y a longtemps dans une simple "vidéo de piratage" pour simuler le code de frappe. ;)
Tout d'abord, la barre n'est pas le seul indicateur de progression de tuyau. L'autre (peut-être même plus connu) est pv (pipe viewer).
Deuxièmement, bar et pv peuvent être utilisés par exemple comme ceci:
$ bar file1 | wc -l
$ pv file1 | wc -l
ou même:
$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l
un truc utile si vous voulez utiliser les commandes bar et pv dans des commandes qui travaillent avec des fichiers donnés en arguments, comme par exemple. copier fichier1 fichier2, doit utiliser traiter la substitution :
$ copy <(bar file1) file2
$ copy <(pv file1) file2
La substitution de processus est une chose magique qui crée des fichiers de tuyau fifo temporaires/dev/fd/et connecte stdout à partir du processus exécuté (entre parenthèses) via ce canal et que la copie le voit comme un fichier ordinaire (à une exception près, il ne peut que le lire en avant).
Mettre à jour:
la commande de barre elle-même permet également la copie. Après l'homme bar:
bar --in-file /dev/rmt/1cbn --out-file \
tape-restore.tar --size 2.4g --buffer-size 64k
Mais la substitution de processus est à mon avis une manière plus générique de le faire. Il utilise le programme cp lui-même.
Ma solution affiche le pourcentage de l'archive est en cours de décompression et d'écriture. J'utilise ceci Lors de l'écriture d'images de 2 Go du système de fichiers racine. Vous avez vraiment besoin d'une barre de progression pour ces choses. Ce que je fais est d'utiliser gzip --list
pour obtenir la taille totale non compressée de l'archive A partir de cela, je calcule le facteur de blocage nécessairepour diviser le fichier en 100 parties. Enfin, j'imprime un message de point de contrôle Pour chaque bloc. Pour un fichier de 2 Go, thisgive environ 10 Mo par bloc. Si cela est trop important, vous pouvez diviser le BLOCKING_FACTOR par 10 ou 100, mais il est alors plus difficile d'imprimer un résultat en pourcentage.
En supposant que vous utilisiez Bash, vous pouvez utiliser la fonction Shell suivante.
untar_progress ()
{
TARBALL=$1
BLOCKING_FACTOR=$(gzip --list ${TARBALL} |
Perl -MPOSIX -ane '$.==2 && print ceil $F[1]/50688')
tar --blocking-factor=${BLOCKING_FACTOR} --checkpoint=1 \
--checkpoint-action='ttyout=Wrote %u% \r' -zxf ${TARBALL}
}
La plupart des commandes unix ne vous donneront pas le type de retour direct à partir duquel vous pouvez le faire .. Certaines vous donneront une sortie que vous pourrez utiliser.
Pour quelque chose comme tar, vous pouvez utiliser le commutateur -v et diriger la sortie vers un programme mettant à jour une petite animation pour chaque ligne lue. Comme tar écrit une liste de fichiers, le programme peut mettre à jour l'animation. Pour compléter un pourcentage, vous devez connaître le nombre de fichiers et compter les lignes.
cp ne donne pas ce genre de sortie pour autant que je sache. Pour surveiller la progression de cp, vous devez surveiller les fichiers source et cible et surveiller la taille de la destination. Vous pourriez écrire un petit programme en utilisant l'appel système stat (2) pour obtenir la taille du fichier. Cela lirait la taille de la source, interrogerait le fichier de destination et mettrait à jour une barre de pourcentage de complétion en fonction de la taille du fichier écrit à ce jour.
Je préfère utiliser dialog avec le --gauge param. Est très souvent utilisé dans les installations de paquetages .deb et autres configurations de base de nombreuses distributions. Donc, vous n'avez pas besoin de réinventer la roue ... à nouveau
Il suffit de mettre une valeur int de 1 à 100 @stdin. Un exemple basique et idiot:
for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done
J'ai ce fichier/bin/Wait (avec chmod u + x perms) pour la cuisson: P
#!/bin/bash
INIT=`/bin/date +%s`
NOW=$INIT
FUTURE=`/bin/date -d "$1" +%s`
[ $FUTURE -a $FUTURE -eq $FUTURE ] || exit
DIFF=`echo "$FUTURE - $INIT"|bc -l`
while [ $INIT -le $FUTURE -a $NOW -lt $FUTURE ]; do
NOW=`/bin/date +%s`
STEP=`echo "$NOW - $INIT"|bc -l`
SLEFT=`echo "$FUTURE - $NOW"|bc -l`
MLEFT=`echo "scale=2;$SLEFT/60"|bc -l`
TEXT="$SLEFT seconds left ($MLEFT minutes)";
TITLE="Waiting $1: $2"
sleep 1s
PTG=`echo "scale=0;$STEP * 100 / $DIFF"|bc -l`
echo $PTG| dialog --title "$TITLE" --gauge "$TEXT" 7 72
done
if [ "$2" == "" ]; then msg="Espera terminada: $1";audio="Listo";
else msg=$2;audio=$2;fi
/usr/bin/notify-send --icon=stock_appointment-reminder-excl "$msg"
espeak -v spanish "$audio"
Donc je peux mettre:
Wait "34 min" "warm up the oven"
ou
Wait "dec 31" "happy new year"
pour moi, plus facile à utiliser et plus esthétique à ce jour est la commande pv
ou bar
par exemple: nécessité de faire une sauvegarde de tout le lecteur avec dd
normalement vous utilisez dd if="$input_drive_path" of="$output_file_path"
avec pv
vous pouvez le faire comme ceci:
dd if="$input_drive_path" | pv | dd of="$output_file_path"
et le progrès va directement à STDOUT
comme ceci:
7.46GB 0:33:40 [3.78MB/s] [ <=> ]
après cela, le résumé arrive
15654912+0 records in
15654912+0 records out
8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
De nombreuses réponses décrivent l’écriture de vos propres commandes pour imprimer '\r' + $some_sort_of_progress_msg
. Le problème est parfois que l'impression de centaines de ces mises à jour par seconde ralentit le processus.
Cependant, si l'un de vos processus produit une sortie (par exemple, 7z a -r newZipFile myFolder
produira chaque nom de fichier au fur et à mesure qu'il le compresse), une solution plus simple, rapide, simple et personnalisable existe.
Installez le module python tqdm
.
$ Sudo pip install tqdm
$ # now have fun
$ 7z a -r -bd newZipFile myFolder | tqdm >> /dev/null
$ # if we know the expected total, we can have a bar!
$ 7z a -r -bd newZipFile myFolder | grep -o Compressing | tqdm --total $(find myFolder -type f | wc -l) >> /dev/null
Aide: tqdm -h
. Un exemple utilisant plus d'options:
$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l
En bonus, vous pouvez également utiliser tqdm
pour envelopper des iterables dans du code python.
EDIT: Pour une version mise à jour, consultez ma page github
Je n'étais pas satisfait des réponses à cette question. Ce que je recherchais personnellement, c’était une barre de progression élégante, comme le voit l’APT.
J'ai jeté un coup d'œil au code source C pour APT et j'ai décidé d'écrire mon propre équivalent pour bash.
Cette barre de progression restera bien en bas du terminal et n'interférera pas avec les sorties envoyées au terminal.
Veuillez noter que la barre est actuellement fixée à 100 caractères de large. Si vous voulez l'adapter à la taille du terminal, c'est assez facile à faire (la version mise à jour sur ma page github gère cela bien).
Je vais poster mon script ici . Exemple d'utilisation:
source ./progress_bar.sh
echo "This is some output"
setup_scroll_area
sleep 1
echo "This is some output 2"
draw_progress_bar 10
sleep 1
echo "This is some output 3"
draw_progress_bar 50
sleep 1
echo "This is some output 4"
draw_progress_bar 90
sleep 1
echo "This is some output 5"
destroy_scroll_area
Le script (je recommande fortement la version sur mon github à la place):
#!/bin/bash
# This code was inspired by the open source C code of the APT progress bar
# http://Bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233
#
# Usage:
# Source this script
# setup_scroll_area
# draw_progress_bar 10
# draw_progress_bar 90
# destroy_scroll_area
#
CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[30m"
COLOR_BG="\e[42m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"
function setup_scroll_area() {
lines=$(tput lines)
let lines=$lines-1
# Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
echo -en "\n"
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Set scroll region (this will place the cursor in the top left)
echo -en "\033[0;${lines}r"
# Restore cursor but ensure its inside the scrolling area
echo -en "$CODE_RESTORE_CURSOR"
echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
# Start empty progress bar
draw_progress_bar 0
}
function destroy_scroll_area() {
lines=$(tput lines)
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Set scroll region (this will place the cursor in the top left)
echo -en "\033[0;${lines}r"
# Restore cursor but ensure its inside the scrolling area
echo -en "$CODE_RESTORE_CURSOR"
echo -en "$CODE_CURSOR_IN_SCROLL_AREA"
# We are done so clear the scroll bar
clear_progress_bar
# Scroll down a bit to avoid visual glitch when the screen area grows by one row
echo -en "\n\n"
}
function draw_progress_bar() {
percentage=$1
lines=$(tput lines)
let lines=$lines
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Move cursor position to last row
echo -en "\033[${lines};0f"
# Clear progress bar
tput el
# Draw progress bar
print_bar_text $percentage
# Restore cursor position
echo -en "$CODE_RESTORE_CURSOR"
}
function clear_progress_bar() {
lines=$(tput lines)
let lines=$lines
# Save cursor
echo -en "$CODE_SAVE_CURSOR"
# Move cursor position to last row
echo -en "\033[${lines};0f"
# clear progress bar
tput el
# Restore cursor position
echo -en "$CODE_RESTORE_CURSOR"
}
function print_bar_text() {
local percentage=$1
# Prepare progress bar
let remainder=100-$percentage
progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");
# Print progress bar
if [ $1 -gt 99 ]
then
echo -ne "${progress_bar}"
else
echo -ne "${progress_bar}"
fi
}
printf_new() {
str=$1
num=$2
v=$(printf "%-${num}s" "$str")
echo -ne "${v// /$str}"
}
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 "$@"
Basé sur le travail d’Edouard Lopez, j’ai créé une barre de progression qui convient à la taille de l’écran, quelle qu’elle soit. Vérifiez-le.
Il est également publié sur Git Hub .
#!/bin/bash
#
# Progress bar by Adriano Pinaffo
# Available at https://github.com/adriano-pinaffo/progressbar.sh
# Inspired on work by Edouard Lopez (https://github.com/edouard-lopez/progress-bar.sh)
# Version 1.0
# Date April, 28th 2017
function error {
echo "Usage: $0 [SECONDS]"
case $1 in
1) echo "Pass one argument only"
exit 1
;;
2) echo "Parameter must be a number"
exit 2
;;
*) echo "Unknown error"
exit 999
esac
}
[[ $# -ne 1 ]] && error 1
[[ $1 =~ ^[0-9]+$ ]] || error 2
duration=${1}
barsize=$((`tput cols` - 7))
unity=$(($barsize / $duration))
increment=$(($barsize%$duration))
skip=$(($duration/($duration-$increment)))
curr_bar=0
prev_bar=
for (( elapsed=1; elapsed<=$duration; elapsed++ ))
do
# Elapsed
prev_bar=$curr_bar
let curr_bar+=$unity
[[ $increment -eq 0 ]] || {
[[ $skip -eq 1 ]] &&
{ [[ $(($elapsed%($duration/$increment))) -eq 0 ]] && let curr_bar++; } ||
{ [[ $(($elapsed%$skip)) -ne 0 ]] && let curr_bar++; }
}
[[ $elapsed -eq 1 && $increment -eq 1 && $skip -ne 1 ]] && let curr_bar++
[[ $(($barsize-$curr_bar)) -eq 1 ]] && let curr_bar++
[[ $curr_bar -lt $barsize ]] || curr_bar=$barsize
for (( filled=0; filled<=$curr_bar; filled++ )); do
printf "▇"
done
# Remaining
for (( remain=$curr_bar; remain<$barsize; remain++ )); do
printf " "
done
# Percentage
printf "| %s%%" $(( ($elapsed*100)/$duration))
# Return
sleep 1
printf "\r"
done
printf "\n"
exit 0
Prendre plaisir
Pour indiquer la progression de l'activité, essayez les commandes suivantes:
while true; do sleep 0.25 && echo -ne "\r\\" && sleep 0.25 && echo -ne "\r|" && sleep 0.25 && echo -ne "\r/" && sleep 0.25 && echo -ne "\r-"; done;
OU
while true; do sleep 0.25 && echo -ne "\rActivity: \\" && sleep 0.25 && echo -ne "\rActivity: |" && sleep 0.25 && echo -ne "\rActivity: /" && sleep 0.25 && echo -ne "\rActivity: -"; done;
OU
while true; do sleep 0.25 && echo -ne "\r" && sleep 0.25 && echo -ne "\r>" && sleep 0.25 && echo -ne "\r>>" && sleep 0.25 && echo -ne "\r>>>"; sleep 0.25 && echo -ne "\r>>>>"; done;
OU
while true; do sleep .25 && echo -ne "\r:Active:" && sleep .25 && echo -ne "\r:aCtive:" && sleep .25 && echo -ne "\r:acTive:" && sleep .25 && echo -ne "\r:actIve:" && sleep .25 && echo -ne "\r:actiVe:" && sleep .25 && echo -ne "\r:activE:"; done;
Vous pouvez utiliser flags/variables à l'intérieur de la boucle while pour vérifier et afficher la valeur/le degré de progression.
J'ai utilisé une réponse de Création d'une chaîne de caractères répétés dans le script shell pour la répétition de caractères. J'ai deux versions relativement petites de bash pour les scripts qui doivent afficher une barre de progression (par exemple, une boucle qui parcourt de nombreux fichiers mais qui n'est pas utile pour les gros fichiers tar ou les opérations de copie). Le plus rapide comprend deux fonctions, l’une pour préparer les chaînes à l’affichage:
preparebar() {
# $1 - bar length
# $2 - bar char
barlen=$1
barspaces=$(printf "%*s" "$1")
barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}
et un pour afficher une barre de progression:
progressbar() {
# $1 - number (-1 for clearing the bar)
# $2 - max number
if [ $1 -eq -1 ]; then
printf "\r $barspaces\r"
else
barch=$(($1*barlen/$2))
barsp=$((barlen-barch))
printf "\r[%.${barch}s%.${barsp}s]\r" "$barchars" "$barspaces"
fi
}
Il pourrait être utilisé comme:
preparebar 50 "#"
ce qui signifie préparer des chaînes pour barre avec 50 "#" caractères, et après cela:
progressbar 35 80
affichera le nombre de caractères "#" correspondant au rapport 35/80:
[##################### ]
Sachez que cette fonction affiche la barre sur la même ligne encore et encore jusqu'à ce que vous (ou un autre programme) imprimiez une nouvelle ligne. Si vous mettez -1 comme premier paramètre, la barre sera effacée:
progressbar -1 80
La version plus lente est tout en une fonction:
progressbar() {
# $1 - number
# $2 - max number
# $3 - number of '#' characters
if [ $1 -eq -1 ]; then
printf "\r %*s\r" "$3"
else
i=$(($1*$3/$2))
j=$(($3-i))
printf "\r[%*s" "$i" | tr ' ' '#'
printf "%*s]\r" "$j"
fi
}
et il peut être utilisé comme (le même exemple que ci-dessus):
progressbar 35 80 50
Si vous avez besoin de progressbar sur stderr, ajoutez simplement >&2
à la fin de chaque commande printf.
Ceci n'est applicable qu'avec gnome zenity. Zenity fournit une excellente interface native aux scripts bash: https://help.gnome.org/users/zenity/stable/
Exemple de barre de progression Zenity:
#!/bin/sh
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "20" ; sleep 1
echo "# Resetting cron jobs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "75" ; sleep 1
echo "# Rebooting system" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
--title="Update System Logs" \
--text="Scanning mail logs..." \
--percentage=0
if [ "$?" = -1 ] ; then
zenity --error \
--text="Update canceled."
fi
J'ai construit sur la réponse fournie par le côté peur
Ceci se connecte à une base de données Oracle pour récupérer la progression d'une restauration RMAN.
#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState($1) and totalState($2)
function ProgressBar {
# Process data
let _progress=(${1}*100/${2}*100)/100
let _done=(${_progress}*4)/10
let _left=40-$_done
# Build progressbar string lengths
_fill=$(printf "%${_done}s")
_empty=$(printf "%${_left}s")
# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%"
}
function rman_check {
sqlplus -s / as sysdba <<EOF
set heading off
set feedback off
select
round((sofar/totalwork) * 100,0) pct_done
from
v\$session_longops
where
totalwork > sofar
AND
opname NOT LIKE '%aggregate%'
AND
opname like 'RMAN%';
exit
EOF
}
# Variables
_start=1
# This accounts as the "totalState" variable for the ProgressBar function
_end=100
_rman_progress=$(rman_check)
#echo ${_rman_progress}
# Proof of concept
#for number in $(seq ${_start} ${_end})
while [ ${_rman_progress} -lt 100 ]
do
for number in _rman_progress
do
sleep 10
ProgressBar ${number} ${_end}
done
_rman_progress=$(rman_check)
done
printf '\nFinished!\n'
#!/bin/bash
function progress_bar() {
bar=""
total=10
[[ -z $1 ]] && input=0 || input=${1}
x="##"
for i in `seq 1 10`; do
if [ $i -le $input ] ;then
bar=$bar$x
else
bar="$bar "
fi
done
#pct=$((200*$input/$total % 2 + 100*$input/$total))
pct=$(($input*10))
echo -ne "Progress : [ ${bar} ] (${pct}%) \r"
sleep 1
if [ $input -eq 10 ] ;then
echo -ne '\n'
fi
}
pourrait créer une fonction qui dessine ceci sur une échelle disons 1-10 pour le nombre de barres
progress_bar 1
echo "doing something ..."
progress_bar 2
echo "doing something ..."
progress_bar 3
echo "doing something ..."
progress_bar 8
echo "doing something ..."
progress_bar 10
J'ai créé une version purement Shell pour un système embarqué en tirant parti des avantages suivants:
La fonction de traitement du signal SIGUSR1 de/usr/bin/dd.
Fondamentalement, si vous envoyez un message 'kill SIGUSR1 $ (pid_of_running_dd_process)', il générera Un résumé de la vitesse de traitement et du montant transféré.
mettre en arrière-plan dd puis l'interroger régulièrement pour obtenir des mises à jour, et générer des ticks de hachage comme les clients ftp de l'ancienne école.
Utilisation de/dev/stdout comme destination pour les programmes non stdout comme scp
Le résultat final vous permet de prendre n’importe quelle opération de transfert de fichier et d’obtenir une mise à jour de progression qui ressemble à une sortie FTP de la vieille école, où vous obtiendrez simplement un signe dièse pour chaque X octets.
Ce n'est pas un code de qualité de production, mais vous voyez l'idée. Je trouve ça mignon
Pour ce que cela vaut, le nombre d'octets réel peut ne pas être reflété correctement dans le nombre de hachages - vous pouvez en avoir un de plus ou moins en fonction des problèmes d'arrondi. N'utilisez pas ceci dans le cadre d'un script de test, c'est juste bon à savoir. Et, oui, je suis conscient que c'est terriblement inefficace - c'est un script Shell et je ne m'en excuse pas.
Exemples avec wget, scp et tftp fournis à la fin. Cela devrait fonctionner avec tout ce qui émet des données. Assurez-vous d’utiliser/dev/stdout pour les programmes qui ne sont pas compatibles avec stdout.
#!/bin/sh
#
# Copyright (C) Nathan Ramella ([email protected]) 2010
# LGPLv2 license
# If you use this, send me an email to say thanks and let me know what your product
# is so I can tell all my friends I'm a big man on the internet!
progress_filter() {
local START=$(date +"%s")
local SIZE=1
local DURATION=1
local BLKSZ=51200
local TMPFILE=/tmp/tmpfile
local PROGRESS=/tmp/tftp.progress
local BYTES_LAST_CYCLE=0
local BYTES_THIS_CYCLE=0
rm -f ${PROGRESS}
dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
| grep --line-buffered -E '[[:digit:]]* bytes' \
| awk '{ print $1 }' >> ${PROGRESS} &
# Loop while the 'dd' exists. It would be 'more better' if we
# actually looked for the specific child ID of the running
# process by identifying which child process it was. If someone
# else is running dd, it will mess things up.
# My PID handling is dumb, it assumes you only have one running dd on
# the system, this should be fixed to just get the PID of the child
# process from the Shell.
while [ $(pidof dd) -gt 1 ]; do
# PROTIP: You can sleep partial seconds (at least on linux)
sleep .5
# Force dd to update us on it's progress (which gets
# redirected to $PROGRESS file.
#
# dumb pid handling again
pkill -USR1 dd
local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))
# Don't print anything unless we've got 1 block or more.
# This allows for stdin/stderr interactions to occur
# without printing a hash erroneously.
# Also makes it possible for you to background 'scp',
# but still use the /dev/stdout trick _even_ if scp
# (inevitably) asks for a password.
#
# Fancy!
if [ $XFER_BLKS -gt 0 ]; then
printf "#%0.s" $(seq 0 $XFER_BLKS)
BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
fi
done
local SIZE=$(stat -c"%s" $TMPFILE)
local NOW=$(date +"%s")
if [ $NOW -eq 0 ]; then
NOW=1
fi
local DURATION=$(($NOW-$START))
local BYTES_PER_SECOND=$(( SIZE / DURATION ))
local KBPS=$((SIZE/DURATION/1024))
local MD5=$(md5sum $TMPFILE | awk '{ print $1 }')
# This function prints out ugly stuff suitable for eval()
# rather than a pretty string. This makes it a bit more
# flexible if you have a custom format (or dare I say, locale?)
printf "\nDURATION=%d\nBYTES=%d\nKBPS=%f\nMD5=%s\n" \
$DURATION \
$SIZE \
$KBPS \
$MD5
}
Exemples:
echo "wget"
wget -q -O /dev/stdout http://www.blah.com/somefile.Zip | progress_filter
echo "tftp"
tftp -l /dev/stdout -g -r something/firmware.bin 192.168.1.1 | progress_filter
echo "scp"
scp [email protected]:~/myfile.tar /dev/stdout | progress_filter
Télécharger un fichier
[##################################################] 100% (137921 / 137921 bytes)
En attente d'un travail pour terminer
[######################### ] 50% (15 / 30 seconds)
Vous pouvez simplement copier-coller dans votre script. Il ne nécessite rien d'autre pour fonctionner.
PROGRESS_BAR_WIDTH=50 # progress bar length in characters
draw_progress_bar() {
# Arguments: current value, max value, unit of measurement (optional)
local __value=$1
local __max=$2
local __unit=${3:-""} # if unit is not supplied, do not display it
# Calculate percentage
if (( $__max < 1 )); then __max=1; fi # anti zero division protection
local __percentage=$(( 100 - ($__max*100 - $__value*100) / $__max ))
# Rescale the bar according to the progress bar width
local __num_bar=$(( $__percentage * $PROGRESS_BAR_WIDTH / 100 ))
# Draw progress bar
printf "["
for b in $(seq 1 $__num_bar); do printf "#"; done
for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH - $__num_bar ))); do printf " "; done
printf "] $__percentage%% ($__value / $__max $__unit)\r"
}
Ici, nous téléchargeons un fichier et redessinons la barre de progression à chaque itération. Peu importe le travail réellement exécuté tant que nous pouvons obtenir 2 valeurs: valeur maximale et valeur actuelle.
Dans l'exemple ci-dessous, la valeur maximale est file_size
et la valeur actuelle est fournie par une fonction et s'appelle uploaded_bytes
.
# Uploading a file
file_size=137921
while true; do
# Get current value of uploaded bytes
uploaded_bytes=$(some_function_that_reports_progress)
# Draw a progress bar
draw_progress_bar $uploaded_bytes $file_size "bytes"
# Check if we reached 100%
if [ $uploaded_bytes == $file_size ]; then break; fi
sleep 1 # Wait before redrawing
done
# Go to the newline at the end of upload
printf "\n"
Si vous devez afficher une barre de progression temporelle (en connaissant à l'avance le temps d'affichage), vous pouvez utiliser Python comme suit:
#!/bin/python
from time import sleep
import sys
if len(sys.argv) != 3:
print "Usage:", sys.argv[0], "<total_time>", "<progressbar_size>"
exit()
TOTTIME=float(sys.argv[1])
BARSIZE=float(sys.argv[2])
PERCRATE=100.0/TOTTIME
BARRATE=BARSIZE/TOTTIME
for i in range(int(TOTTIME)+1):
sys.stdout.write('\r')
s = "[%-"+str(int(BARSIZE))+"s] %d%% "
sys.stdout.write(s % ('='*int(BARRATE*i), int(PERCRATE*i)))
sys.stdout.flush()
SLEEPTIME = 1.0
if i == int(TOTTIME): SLEEPTIME = 0.1
sleep(SLEEPTIME)
print ""
Ensuite, en supposant que vous avez enregistré le script Python en tant que progressbar.py
, il est possible d'afficher la barre de progression à partir de votre script bash en exécutant la commande suivante:
python progressbar.py 10 50
Il afficherait une barre de progression de taille 50
caractères et "en cours d'exécution" pendant 10
secondes.
#!/bin/bash
tot=$(wc -c /proc/$$/fd/255 | awk '/ /{print $1}')
now() {
echo $(( 100* ($(awk '/^pos:/{print $2}' < /proc/$$/fdinfo/255)-166) / (tot-166) )) "%"
}
now;
now;
now;
now;
now;
now;
now;
now;
now;
sortie:
0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %
remarque: si au lieu de 255, vous mettez 1, vous surveillez la norme en entrée ... avec 2 en sortie standard (mais vous devez modifier la source pour définir "tot" sur la taille du fichier de sortie projetée)