No he visto nada similar y todas las funciones personalizadas aquí parecen centrarse solo en el renderizado, así que ... mi muy simple solución compatible con POSIX a continuación con explicaciones paso a paso porque esta pregunta no es trivial.
TL; DR
Renderizar la barra de progreso es muy fácil. Estimar cuánto debería renderizar es una cuestión diferente. Así es como se representa (anima) la barra de progreso: puede copiar y pegar este ejemplo en un archivo y ejecutarlo:
#!/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}
- valores del 1 al 20
echo -n
- imprimir sin nueva línea al final
echo -e
- interpretar caracteres especiales durante la impresión
"\r"
- retorno de carro, un carácter especial para volver al comienzo de la línea
Puede hacer que reproduzca cualquier contenido a cualquier velocidad, por lo que este método es muy universal, por ejemplo, se usa a menudo para visualizar "piratería" en películas tontas, no es broma.
Respuesta completa
El meollo del problema es cómo determinar el $i
valor, es decir, cuánto de la barra de progreso para mostrar. En el ejemplo anterior, simplemente dejé que se incremente en for
bucle para ilustrar el principio, pero una aplicación de la vida real usaría un bucle infinito y calcularía la $i
variable en cada iteración. Para realizar dicho cálculo necesita los siguientes ingredientes:
- cuanto trabajo hay que hacer
- cuánto trabajo se ha hecho hasta ahora
En caso de cp
que necesite el tamaño de un archivo fuente y el tamaño del archivo de destino:
#!/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
- verifique las estadísticas del archivo
-c
- valor devuelto formateado
%s
- tamaño total
En el caso de operaciones como el desempaquetado de archivos, calcular el tamaño de la fuente es un poco más difícil pero igual de fácil que obtener el tamaño de un archivo sin comprimir:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l
- muestra información sobre el archivo zip
tail -n1
- trabajar con 1 línea desde abajo
tr -s ' '
- traducir múltiples espacios a uno (exprimirlos)
cut -d' ' -f3
- corte la tercera columna delimitada por espacios
Aquí está la carne del problema, sin embargo. Esta solución es cada vez menos general. Todos los cálculos del progreso real están estrechamente vinculados al dominio que está tratando de visualizar, es una operación de un solo archivo, una cuenta regresiva del temporizador, un número creciente de archivos en un directorio, operación en múltiples archivos, etc., por lo tanto, No se puede reutilizar. La única parte reutilizable es la representación de la barra de progreso. Para reutilizarlo, debe abstraerlo y guardarlo en un archivo (por ejemplo /usr/lib/progress_bar.sh
), luego definir funciones que calculen los valores de entrada específicos de su dominio. Así es como podría verse un código generalizado (también hice la $BAR
dinámica porque la gente lo pedía, el resto ya debería estar claro):
#!/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 archivo incorporado para imprimir cosas en un formato dado
printf '%50s'
- no imprima nada, rellene con 50 espacios
tr ' ' '#'
- traduce cada espacio al signo hash
Y así es como lo usarías:
#!/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
Obviamente, puede envolverse en una función, reescribirse para trabajar con flujos entubados, reescribirse en otro idioma, sea cual sea su veneno.