Je n'ai rien vu de similaire et toutes les fonctions personnalisées semblent se concentrer uniquement sur le rendu, alors ... ma solution très simple conforme POSIX ci-dessous avec des explications étape par étape car cette question n'est pas triviale.
TL; DR
Le rendu de la barre de progression est très simple. L'estimation de la quantité à rendre doit être différente. Voici comment rendre (animer) la barre de progression - vous pouvez copier et coller cet exemple dans un fichier et l'exécuter:
#!/bin/sh
BAR='####################' # this is full bar, e.g. 20 chars
for i in {1..20}; do
echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
sleep .1 # wait 100ms between "frames"
done
{1..20}
- valeurs de 1 à 20
echo -n
- imprimer sans nouvelle ligne à la fin
echo -e
- interpréter des caractères spéciaux lors de l'impression
"\r"
- retour chariot, un caractère spécial pour revenir au début de la ligne
Vous pouvez lui faire rendre n'importe quel contenu à n'importe quelle vitesse, donc cette méthode est très universelle, par exemple souvent utilisée pour la visualisation de "piratage" dans des films stupides, sans blague.
Réponse complète
Le gros du problème est de savoir comment déterminer la $i
valeur, c'est-à-dire quelle proportion de la barre de progression afficher. Dans l'exemple ci-dessus, je l'ai simplement laissé incrémenter en for
boucle pour illustrer le principe, mais une application réelle utiliserait une boucle infinie et calculerait la $i
variable à chaque itération. Pour effectuer ce calcul, il a besoin des ingrédients suivants:
- combien de travail il reste à faire
- combien de travail a été fait jusqu'à présent
Dans le cas où cp
il a besoin de la taille d'un fichier source et de la taille du fichier cible:
#!/bin/sh
$src=/path/to/source/file
$tgt=/path/to/target/file
cp "$src" "$tgt" & # the & forks the `cp` process so the rest
# of the code runs without waiting (async)
BAR='####################'
src_size=$(stat -c%s "$src") # how much there is to do
while true; do
tgt_size=$(stat -c%s "$tgt") # how much has been done so far
i=$(( $tgt_size * 20 / $src_size ))
echo -ne "\r${BAR:0:$i}"
if [ $tgt_size == $src_size ]; then
echo "" # add a new line at the end
break; # break the loop
fi
sleep .1
done
stat
- vérifier les statistiques des fichiers
-c
- retourne une valeur formatée
%s
- taille totale
Dans le cas d'opérations comme le décompression de fichiers, le calcul de la taille de la source est légèrement plus difficile mais toujours aussi simple que d'obtenir la taille d'un fichier non compressé:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l
- afficher des informations sur l'archive zip
tail -n1
- travailler avec 1 ligne à partir du bas
tr -s ' '
- traduire plusieurs espaces en un (les presser)
cut -d' ' -f3
- couper la 3e colonne délimitée par des espaces
Voici la chair du problème, cependant. Cette solution est de moins en moins générale. Tous les calculs de la progression réelle sont étroitement liés au domaine que vous essayez de visualiser, est-ce une opération sur un seul fichier, un compte à rebours, un nombre croissant de fichiers dans un répertoire, une opération sur plusieurs fichiers, etc., par conséquent, il ne peut pas être réutilisé. La seule partie réutilisable est le rendu de la barre de progression. Pour le réutiliser, vous devez l'abstraire et l'enregistrer dans un fichier (par exemple /usr/lib/progress_bar.sh
), puis définir des fonctions qui calculent les valeurs d'entrée spécifiques à votre domaine. Voici à quoi pourrait ressembler un code généralisé (j'ai aussi fait la $BAR
dynamique parce que les gens le demandaient, le reste devrait être clair maintenant):
#!/bin/sh
BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)
work_todo=$(get_work_todo) # how much there is to do
while true; do
work_done=$(get_work_done) # how much has been done so far
i=$(( $work_done * $BAR_length / $work_todo ))
echo -ne "\r${BAR:0:$i}"
if [ $work_done == $work_todo ]; then
echo ""
break;
fi
sleep .1
done
printf
- un intégré pour imprimer des trucs dans un format donné
printf '%50s'
- n'imprimez rien, remplissez-le de 50 espaces
tr ' ' '#'
- traduire chaque espace en signe de hachage
Et voici comment vous l'utiliseriez:
#!/bin/sh
src=/path/to/source/file
tgt=/path/to/target/file
function get_work_todo() {
echo $(stat -c%s "$src")
}
function get_work_done() {
[ -e "$tgt" ] && # if target file exists
echo $(stat -c%s "$tgt") || # echo its size, else
echo 0 # echo zero
}
cp "$src" "$tgt" & # copy in the background
source /usr/lib/progress_bar.sh # execute the progress bar
Évidemment, il peut être enveloppé dans une fonction, réécrit pour fonctionner avec des flux canalisés, réécrit dans une autre langue, quel que soit votre poison.