¿Cómo agregar una barra de progreso a un script de shell?
Pregunta
Cuando se ejecutan scripts en bash o cualquier otro shell en * NIX, mientras se ejecuta un comando que toma más de unos pocos segundos, se necesita una barra de progreso.
Por ejemplo, copiando un archivo grande, abriendo un archivo tar grande.
¿Qué formas recomienda para agregar barras de progreso a los scripts de shell?
Solución
Puedes implementar esto sobrescribiendo una línea. Utilice \ r
para volver al principio de la línea sin escribir \ n
en el terminal.
Escriba \ n
cuando haya terminado para avanzar en la línea.
Utilice echo -ne
para:
- no imprime
\ n
y - para reconocer secuencias de escape como
\ r
.
Aquí hay una demostración:
echo -ne '##### (33%)\r'
sleep 1
echo -ne '############# (66%)\r'
sleep 1
echo -ne '####################### (100%)\r'
echo -ne '\n'
En un comentario a continuación, puk menciona que " falla " si comienza con una línea larga y luego desea escribir una línea corta: en este caso, deberá sobrescribir la longitud de la línea larga (por ejemplo, con espacios).
Otros consejos
También te puede interesar cómo hacer un spinner :
¿Puedo hacer un giro en Bash?
¡Claro!
i=1 sp="/-\|" echo -n ' ' while true do printf "\b${sp:i++%${#sp}:1}" done
Cada vez que el bucle se repite, muestra el siguiente carácter en el sp Cadena, envolviéndose a medida que llega al final. (i es la posición de el caracter actual a mostrar y $ {# sp} es la longitud del sp cadena).
La cadena \ b se reemplaza por un carácter de 'retroceso'. Alternativamente, puedes jugar con \ r para volver al principio de la línea.
Si desea que se ralentice, coloque un comando de suspensión dentro del bucle (después de la impresión).
Un equivalente de POSIX sería:
sp='/-\|' printf ' ' while true; do printf '\b%.1s' "$sp" sp=${sp#?}${sp%???} done
Si ya tiene un bucle que hace mucho trabajo, puede llamar al siguiente función al comienzo de cada iteración para actualizar el hilandero:
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
Algunas publicaciones han mostrado cómo mostrar el progreso del comando. Para calcularlo, deberás ver cuánto has progresado. En los sistemas BSD, algunos comandos, como dd (1), aceptan una señal de SIGINFO
, e informarán de su progreso. En los sistemas Linux, algunos comandos responderán de manera similar a SIGUSR1
. Si esta instalación está disponible, puede canalizar su entrada a través de dd
para monitorear el número de bytes procesados.
Alternativamente, puede usar lsof
para obtener el desplazamiento de el puntero de lectura del archivo, y por lo tanto calcular el progreso. He escrito un comando, denominado pmonitor , que muestra el progreso del procesamiento de un proceso o archivo específico. Con él puedes hacer cosas, como las siguientes.
$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%
Una versión anterior de los scripts de shell de Linux y FreeBSD aparece en mi blog .
usa el comando linux pv:
no sabe el tamaño si está en el medio de la secuencia, pero le da una velocidad y un total, y desde allí puede averiguar cuánto tiempo debería tardar y obtener retroalimentación para que sepa que no se ha bloqueado.
Obtuve una función de barra de progreso fácil que escribí el otro día:
#!/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- Obtuve una función de barra de progreso fácil que escribí el otro día:
<*>
O engancharlo de,
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'
O engancharlo de,
https://github.com/fearside/ProgressBar/
Estaba buscando algo más sexy que la respuesta seleccionada, y también mi propio script.
Vista previa
Fuente
Lo puse en 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
}
Uso
progress-bar 100
GNU tar tiene una opción útil que ofrece una funcionalidad de una barra de progreso simple.
(...) Otra acción de punto de control disponible es "punto" (o "."). Le indica a tar que imprima un solo punto en el flujo de listado estándar, por ejemplo:
$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
El mismo efecto se puede obtener mediante:
$ tar -c --checkpoint=.1000 /var
Un método más simple que funciona en mi sistema usando la utilidad 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
También me gustaría contribuir con mi progreso propio barra
Alcanza la precisión de los subcaracteres mediante el uso de Bloques de mitad Unicode
El código está incluido
Esto le permite visualizar que un comando todavía se está ejecutando:
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
Esto creará un bucle de tiempo infinito que se ejecuta en segundo plano y se hace eco de ". " cada segundo. Esto mostrará .
en el shell. Ejecute el comando tar
o cualquier comando que desee. Cuando el comando termine de ejecutarse, elimine el último trabajo que se está ejecutando en segundo plano, que es el bucle infinito
No he visto nada similar, así que ... mi solución muy 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
: imprimir sin una nueva línea al final -
echo -e
: interpreta caracteres especiales al imprimir -
" \ r "
: retorno de carro, un carácter especial para volver al principio de la línea
Lo usé hace mucho tiempo en un simple " video de hacking " para simular la escritura de código. ;)
La mayoría de los comandos de Unix no te darán el tipo de respuesta directa desde la que puedes hacer esto. Algunos te darán salida en stdout o stderr que puedes usar.
Para algo como tar, puedes usar el interruptor -v y canalizar la salida a un programa que actualiza una pequeña animación para cada línea que lee. A medida que tar escribe una lista de archivos, el programa puede actualizar la animación. Para completar un porcentaje, deberías saber la cantidad de archivos y contar las líneas.
cp no da este tipo de salida por lo que sé. Para monitorear el progreso de cp, tendría que monitorear los archivos de origen y destino y ver el tamaño del destino. Puede escribir un programa en C pequeño usando la llamada al sistema stat (2) para obtener el tamaño del archivo. Esto leería el tamaño del origen, luego sondearía el archivo de destino y actualizaría una barra de% completa en función del tamaño del archivo escrito hasta la fecha.
Mi solución muestra el porcentaje de tarball que
Actualmente está siendo descomprimido y escrito. yo uso esto
Al escribir 2 GB de imágenes del sistema de archivos raíz. De verdad
Necesito una barra de progreso para estas cosas. Lo que hago es usar
gzip --list
para obtener el tamaño total sin comprimir de
tarball A partir de eso calculo el factor de bloqueo necesario.
Para dividir el archivo en 100 partes. Finalmente, imprimo un
Mensaje de punto de control para cada bloque. Para un archivo de 2GB este
Da unos 10MB por bloque. Si eso es demasiado grande, entonces puedes
divide el BLOQUEO_FACTOR por 10 o 100, pero entonces es
es más difícil imprimir una salida bonita en términos de porcentaje.
Suponiendo que está utilizando Bash, entonces puede usar el siguiente función de shell
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}
}
En primer lugar, la barra no es el único medidor de progreso de tubería. El otro (tal vez incluso más conocido) es pv (visor de tuberías).
En segundo lugar, bar y pv se pueden usar, por ejemplo, de esta forma:
$ bar file1 | wc -l
$ pv file1 | wc -l
o incluso:
$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l
un truco útil si quieres usar bar y pv en comandos que trabajan con archivos dados en argumentos, como p. ej. copiar archivo1 archivo2, es utilizar sustitución de proceso :
$ copy <(bar file1) file2
$ copy <(pv file1) file2
La sustitución de procesos es una cosa mágica de bash que crea archivos de pipe / dev / fd / fifo temporales y conecta la salida estándar del proceso ejecutado (entre paréntesis) a través de este pipe y copy lo ve como un archivo ordinario (con una excepción, puede sólo léelo hacia delante).
Actualización:
el propio comando de barra también permite copiar. Después de la barra de hombre:
bar --in-file /dev/rmt/1cbn --out-file \
tape-restore.tar --size 2.4g --buffer-size 64k
Pero la sustitución de procesos es, en mi opinión, una forma más genérica de hacerlo. Una utiliza el programa cp en sí.
Prefiero usar dialog con el parámetro --gauge . Se usa muy a menudo en las instalaciones de paquetes .deb y otras cosas de configuración básica de muchas distribuciones. Así que no necesitas reinventar la rueda ... otra vez
Simplemente ponga un valor int de 1 a 100 @stdin. Un ejemplo básico y tonto:
for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done
Tengo este archivo / bin / Wait (con chmod u + x perms) para cocinar: 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"
Así que puedo poner:
Espere " 34 min " " calentar el horno "
o
Espere " diciembre 31 " " feliz año nuevo "
Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
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 (( Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
# 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"
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_max < 1 )); then __max=1; fi # anti zero division protection
local __percentage=$(( 100 - ( Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_max*100 - Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_value*100) / Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_max ))
# Rescale the bar according to the progress bar width
local __num_bar=$(( Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_percentage * $PROGRESS_BAR_WIDTH / 100 ))
# Draw progress bar
printf "["
for b in $(seq 1 Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_num_bar); do printf "#"; done
for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH - Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_num_bar ))); do printf " "; done
printf "] Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_percentage%% ( Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_value / Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_max Aquí es cómo podría verse
Cargar un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando que se complete un trabajo
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa
Puedes copiarlo y pegarlo en tu script. No requiere nada más para funcionar.
<*>
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
<*>_unit)\r"
}
Ejemplo de uso
Aquí, cargamos un archivo y volvemos a dibujar la barra de progreso en cada iteración. No importa qué trabajo se realice realmente siempre que podamos obtener 2 valores: valor máximo y valor actual.
En el siguiente ejemplo, el valor máximo es file_size
y el valor actual lo proporciona alguna función y se llama uploaded_bytes
.
Barra de progreso de estilo APT (no interrumpe la salida normal)
EDITAR: para obtener una versión actualizada, consulte mi página de github
No estaba satisfecho con las respuestas a esta pregunta. Lo que buscaba personalmente era una barra de progreso elegante como la ve la APT.
Eché un vistazo al código fuente de C para APT y decidí escribir mi propio equivalente para bash.
Esta barra de progreso se mantendrá bien en la parte inferior del terminal y no interferirá con ninguna salida enviada al terminal.
Tenga en cuenta que la barra está actualmente fija en 100 caracteres de ancho. Si quieres escalarlo al tamaño del terminal, esto también es bastante fácil de lograr (la versión actualizada en mi página de github maneja esto bien).
Publicaré mi script aquí. Ejemplo de uso:
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
El script (recomiendo la versión en mi github):
#!/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}"
}
para mí, el más fácil de usar y de mejor apariencia hasta ahora es el comando pv
o barra
como si alguien hubiera escrito
por ejemplo: necesita hacer una copia de seguridad de toda la unidad con dd
normalmente usas dd if = " $ input_drive_path " of = " $ output_file_path "
con pv
puedes hacerlo así:
dd if = " $ input_drive_path " | pv | dd of = " $ output_file_path "
y el progreso va directamente a STDOUT
como esto:
7.46GB 0:33:40 [3.78MB/s] [ <=> ]
una vez hecho esto, aparece el resumen
15654912+0 records in
15654912+0 records out
8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
Muchas respuestas describen cómo escribir sus propios comandos para imprimir '\ r' + $ some_sort_of_progress_msg
. El problema a veces es que la impresión de cientos de estas actualizaciones por segundo ralentizará el proceso.
Sin embargo, si alguno de sus procesos produce resultados (por ejemplo, 7z a -r newZipFile myFolder
generará cada nombre de archivo a medida que lo comprima), entonces existe una solución más simple, rápida, indolora y personalizable.
Instale el módulo de 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
Ayuda: tqdm -h
. Un ejemplo usando más opciones:
$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l
Como bonificación, también puedes usar tqdm
para envolver los iterables en código python.
https://github.com/tqdm/tqdm/blob/ master / README.rst # module
Esto solo es aplicable usando gnome zenity. Zenity proporciona una gran interfaz nativa para golpear scripts: https://help.gnome.org/users/zenity/stable/
Ejemplo de la barra de progreso 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
Utilicé una respuesta de Creando cadenas de caracteres repetidos en un script de shell para la repetición de char. Tengo dos versiones relativamente pequeñas de bash para los scripts que necesitan mostrar la barra de progreso (por ejemplo, un bucle que atraviesa muchos archivos, pero no es útil para grandes archivos de alquitrán u operaciones de copia). La más rápida consta de dos funciones, una para preparar las cadenas para la visualización de la barra:
preparebar() {
# $1 - bar length
# $2 - bar char
barlen=$1
barspaces=$(printf "%*s" "$1")
barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}
y uno para mostrar una barra de progreso:
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
}
Se podría usar como:
preparebar 50 "#"
que significa preparar cadenas para la barra con 50 " # " personajes, y después de eso:
progressbar 35 80
mostrará el número de " # " Caracteres que corresponden a la relación 35/80:
[##################### ]
Tenga en cuenta que la función muestra la barra en la misma línea una y otra vez hasta que usted (o algún otro programa) imprima una nueva línea. Si coloca -1 como primer parámetro, la barra se borraría:
progressbar -1 80
La versión más lenta está en una sola función:
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
}
y se puede usar como (el mismo ejemplo anterior):
progressbar 35 80 50
Si necesita la barra de progreso en stderr, simplemente agregue > & amp; 2
al final de cada comando printf.
Para indicar el progreso de la actividad, pruebe los siguientes comandos:
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;
OR
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;
OR
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;
OR
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;
Uno puede usar indicadores / variables dentro del bucle while para verificar y mostrar el valor / grado del progreso.
Sobre la base del trabajo de Edouard Lopez, creé una barra de progreso que se ajusta al tamaño de la pantalla, sea cual sea. Échale un vistazo.
También se publicó en 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
Disfruta
Usando las sugerencias enumeradas anteriormente, decidí implementar mi propia barra de progreso.
#!/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 "$@"
Hice una versión de shell puro para un sistema integrado aprovechando:
-
/ usr / bin / dd función de manejo de señales SIGUSR1.
Básicamente, si envías un 'kill SIGUSR1 $ (pid_of_running_dd_process)', saldrá un resumen de la velocidad de procesamiento y la cantidad transferida.
-
en segundo plano dd y luego consultándolo regularmente para actualizaciones, y generando hash ticks como solían hacer los clientes ftp de la vieja escuela.
-
Usando / dev / stdout como destino para programas que no son compatibles con la versión estándar como scp
El resultado final le permite tomar cualquier operación de transferencia de archivos y obtener una actualización de progreso que se parece a la salida 'hash' de FTP de la vieja escuela, donde solo obtendría una marca de control por cada X bytes.
Esto no es un código de calidad de producción, pero entiendes la idea. Pienso que es lindo.
Para lo que vale, es posible que el número real de bytes no se refleje correctamente en el número de hashes; es posible que tenga uno más o menos según los problemas de redondeo. No utilices esto como parte de un guión de prueba, es solo una sorpresa. Y, sí, soy consciente de que esto es terriblemente ineficiente: es un script de shell y no me disculpo por ello.
Ejemplos con wget, scp y tftp al final. Debería funcionar con cualquier cosa que tenga datos emitidos. Asegúrate de usar / dev / stdout para los programas que no son aptos para 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
}
Ejemplos:
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
En caso de que tenga que mostrar una barra de progreso temporal (sabiendo de antemano el tiempo de visualización), puede usar Python de la siguiente manera:
#!/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 ""
Luego, suponiendo que guardó el script de Python como progressbar.py
, es posible mostrar la barra de progreso de su script de bash ejecutando el siguiente comando:
python progressbar.py 10 50
Mostraría caracteres 50
de tamaño de barra de progreso y " en ejecución " para 10
segundos.
Me he basado en la respuesta proporcionada por fearside
Esto se conecta a una base de datos Oracle para recuperar el progreso de una restauración 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- Me he basado en la respuesta proporcionada por fearside
Esto se conecta a una base de datos Oracle para recuperar el progreso de una restauración 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
}
podría crear una función que dibuje esto en una escala, por ejemplo, 1-10 para el número de barras:
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;
salida:
0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %
nota: si en lugar de 255 pone 1, monitoreará el estándar en ... con 2 fuera del estándar (pero debe modificar la fuente para establecer " tot " al tamaño del archivo de salida proyectado)