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