521 votes

Comment ajouter une barre de progression à un script shell?

Lorsque vous écrivez un script en bash ou dans tout autre shell sous *NIX, lors de l'exécution d'une commande qui prendra plus que quelques secondes, une barre de progression est nécessaire.

Par exemple, lors de la copie d'un gros fichier, de l'ouverture d'un gros fichier tar.

Quelles méthodes recommandez-vous pour ajouter des barres de progression aux scripts shell ?

0 votes

Voir aussi stackoverflow.com/questions/12498304/… pour des exemples de la logique de contrôle (mettre un travail en arrière-plan et faire quelque chose jusqu'à ce qu'il se termine).

4 votes

Il existe un ensemble de besoins que nous trouvons souvent utiles lors de la rédaction de scripts. journalisation, affichage de la progression, couleurs, sorties sophistiquées, etc. J'ai toujours pensé qu'il devrait y avoir une sorte de structure de script simple. Finalement, j'ai décidé d'en implémenter une puisque je n'en ai trouvé aucune. Vous pourriez trouver cela utile. C'est en pur bash, je veux dire Juste Bash. github.com/SumuduLansakara/JustBash

0 votes

Ne devrait-il pas être déplacé vers unix.stackexchange.com ?

783voto

Mitch Haile Points 5059

Vous pouvez implémenter cela en écrasant une ligne. Utilisez \r pour revenir au début de la ligne sans écrire \n dans le terminal.

Écrivez \n lorsque vous avez fini pour avancer la ligne.

Utilisez echo -ne pour :

  1. ne pas imprimer \n et
  2. reconnaître les séquences d'échappement comme \r.

Voici une démonstration :

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

25 votes

Selon la page de manuel de echo (au moins sur MacOS X), sh/bash utilise sa propre commande intégrée echo qui n'accepte pas le "-n" ... donc pour réaliser la même chose, vous devez mettre \r\c à la fin de la chaîne, au lieu de juste \r

1 votes

Je pense que vous voulez dire que la version Mac ne prend pas -e? Vous avez raison, -e semble être une extension GNU.

61 votes

La manière portable de produire ceci est d'utiliser printf au lieu de echo.

110voto

Daenyth Points 11297

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

Puis-je faire un spinner en Bash?

Bien sûr!

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

À chaque itération de la boucle, le prochain caractère dans la chaîne sp est affiché, en revenant au début une fois à la fin. (i est la position du 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 'backspace'. En alternative, vous pourriez utiliser \r pour revenir au début de la ligne.

Si vous souhaitez ralentir, ajoutez 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 effectue beaucoup de travail, vous pouvez appeler la fonction suivante au début de chaque itération pour 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

26 votes

Version beaucoup plus courte, entièrement portable : while :;faire pour s in / - \\ \|; faire printf "\r$s";sleep .1;fait;fait (*: sleep peut nécessiter des entiers plutôt que des décimaux)

1 votes

@Daenyth. Merci. Où devrions-nous appeler la commande que nous devons surveiller son progrès en utilisant le code précédent?

0 votes

@goro: Dans la ligne some_work ... ci-dessus; une discussion plus détaillée qui s'appuie sur cette réponse utile et le commentaire utile d'Adam Katz - avec un accent sur la conformité POSIX - peut être trouvée ici.

58voto

Seth Wegner Points 49

Utilisez la commande Linux pv.

Il ne connaît pas la taille s'il se trouve au milieu du pipeline, mais il donne une vitesse et un total, et à partir de là vous pouvez déterminer combien de temps cela devrait prendre et obtenir une rétroaction pour savoir qu'il ne s'est pas bloqué.

53voto

mishunika Points 1041

Vous pouvez utiliser ce projet : http://www.theiling.de/projects/bar.html

ressemble à ceci : barre de progression

49voto

Diomidis Spinellis Points 8417

Certains articles ont montré comment afficher la progression de la commande. Pour la calculer, vous devrez voir combien vous avez avancé. Sur les systèmes BSD, certaines commandes, telles que dd (1), acceptent un signal SIGINFO et indiqueront leur progression. Sur les systèmes Linux, certaines commandes répondront de manière similaire à SIGUSR1. Si cette fonctionnalité est disponible, vous pouvez rediriger votre entrée via _dd pour surveiller le nombre d'octets traités.

Alternativement, vous pouvez utiliser lsof pour obtenir le décalage du pointeur de lecture du fichier, et ainsi calculer la progression. Voici un exemple d'utilisation de lsof (1) pour voir la progression de wc (1) en train de lire un gros fichier nommé blob.

$ wc -l blob &
[1] 3405769

$ lsof -w -o0 -o -c wc
COMMAND     PID USER   FD   TYPE  DEVICE       OFFSET     NODE NAME
[...]
wc      3405769  dds    3r   REG   254,7 0t2656059392  7733716 blob

J'ai écrit une commande, nommée pmonitor, qui affiche la progression du traitement d'un processus ou fichier spécifié. Avec elle, vous pouvez faire des choses, comme ci-dessous.

$ 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 ("Suivre la progression d'un processus sur Unix").

0 votes

C'est génial, j'oublie toujours de passer les choses par pv :-) Je pense que ma commande "stat" fonctionne un peu différemment, ma version (Linux) de ce script: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b

0 votes

Veuillez citer les parties pertinentes du code dans votre réponse comme demandé sur cette page d'aide : stackoverflow.com/help/how-to-answer

0 votes

@cpm J'ai cité le titre du lien. Si vous pensez qu'autre chose est nécessaire, veuillez être plus précis.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X