bash - scripts - ¿Cómo agregar una barra de progreso a un script de shell?
sentencias de control en shell script (30)
Al ejecutar scripts en bash o cualquier otro shell en * NIX, al ejecutar un comando que llevará 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?
Aquí es cómo podría verse
Subiendo un archivo
[##################################################] 100% (137921 / 137921 bytes)
Esperando un trabajo para completar
[######################### ] 50% (15 / 30 seconds)
Función simple que la implementa.
Puedes copiarlo y pegarlo en tu script. No requiere nada más para trabajar.
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 (( $__max < 1 )); then __max=1; fi # anti zero division protection
local __percentage=$(( 100 - ($__max*100 - $__value*100) / $__max ))
# Rescale the bar according to the progress bar width
local __num_bar=$(( $__percentage * $PROGRESS_BAR_WIDTH / 100 ))
# Draw progress bar
printf "["
for b in $(seq 1 $__num_bar); do printf "#"; done
for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH - $__num_bar ))); do printf " "; done
printf "] $__percentage%% ($__value / $__max $__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
.
# 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"
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 SIGINFO
e informarán sobre 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 del puntero de lectura del archivo, y así calcular el progreso. He escrito un comando, llamado 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 .
Basándome en el trabajo de Edouard López, creé una barra de progreso que se ajusta al tamaño de la pantalla, sea cual sea. Echale un vistazo.
También está publicado 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: $0 [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
Disfrutar
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, asumiendo que guardó el script Python como progressbar.py
, es posible mostrar la barra de progreso desde su script bash ejecutando el siguiente comando:
python progressbar.py 10 50
Mostraría una barra de progreso con un tamaño de 50
caracteres y "correr" durante 10
segundos.
En primer lugar, Bar 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 manera:
$ 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 desea utilizar bar y pv en comandos que trabajan con archivos dados en argumentos, como por ejemplo copiar archivo1 archivo2, es usar la sustitución de procesos :
$ copy <(bar file1) file2
$ copy <(pv file1) file2
La sustitución de procesos es una cosa de magia 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, solo puede leerlo hacia adelante).
Actualizar:
El propio comando de barra permite también 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. Un programa usa el propio cp.
Estaba buscando algo más sexy que la respuesta seleccionada, también lo hizo mi propio guión.
Avance
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
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 while infinito que se ejecuta en segundo plano y hace eco de un "." cada segundo. Esto se mostrará .
en la concha Ejecute el comando tar
o cualquier comando que desee. Cuando ese comando termina de ejecutarse, finalice el último trabajo que se ejecuta en segundo plano, que es el bucle infinito while .
Esto solo es aplicable usando gnome zenity. Zenity proporciona una excelente interfaz nativa para bash 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
He construido sobre la respuesta proporcionada por el miedo
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-$_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''
Hice una versión de shell puro para un sistema integrado aprovechando:
La función de manejo de señal SIGUSR1 de / usr / bin / dd.
Básicamente, si envía un ''kill SIGUSR1 $ (pid_of_running_dd_process)'', obtendrá un resumen de la velocidad de procesamiento y la cantidad transferida.
en segundo plano dd y luego consultándolo regularmente para actualizaciones, y generando tic tac de hash como solían hacer los clientes ftp de la vieja escuela.
Usando / dev / stdout como destino para programas amigables no-stdout como scp
El resultado final le permite realizar cualquier operación de transferencia de archivos y obtener una actualización de progreso que se parece a la salida de "hash" del 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, el número real de bytes no se refleja 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 emitados. Asegúrate de usar / dev / stdout para programas que no sean amigables con stdout.
#!/bin/sh
#
# Copyright (C) Nathan Ramella ([email protected]) 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 [email protected]:~/myfile.tar /dev/stdout | progress_filter
La mayoría de los comandos de Unix no te darán el tipo de retroalimentación directa desde la que puedes hacer esto. Algunos te darán salida en stdout o stderr que puedes usar.
Para algo como tar, puede 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 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 se está descomprimiendo y escribiendo. Uso esto cuando escribo imágenes de sistema de archivos raíz de 2GB. Realmente necesitas una barra de progreso para estas cosas. Lo que hago es usar gzip --list
para obtener el tamaño total sin comprimir del archivo comprimido. 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 esto da aproximadamente 10MB por bloque. Si eso es demasiado grande, puede dividir BLOCKING_FACTOR por 10 o 100, pero luego es más difícil imprimir una buena cantidad en términos de porcentaje.
Asumiendo que está utilizando Bash, entonces puede usar la 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}
}
Muchas respuestas describen cómo escribir sus propios comandos para imprimir ''/r'' + $some_sort_of_progress_msg
. El problema a veces es que imprimir 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
emitirá 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 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 tqdm
adicional, también puede usar tqdm
para envolver iterables en código python.
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 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 piratería" para simular el código de escritura. ;)
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;
O
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;
O
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;
O
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;
Se pueden usar indicadores / variables dentro del bucle while para verificar y mostrar el valor / grado del progreso.
Para mí, el más fácil de usar y el que más se ve es el comando pv
o el bar
como lo escribió un chico
por ejemplo: necesita hacer una copia de seguridad de todo el disco 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] [ <=> ]
después de que se hace el resumen aparece
15654912+0 records in
15654912+0 records out
8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
Prefiero usar el diálogo 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 / bin / Wait file (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:
Wait "34 min" "warm up the oven"
o
Wait "dec 31" "happy new year"
También me gustaría aportar mi propia barra de progreso.
Alcanza la precisión de los subcaracteres utilizando bloques Half Unicode.
El codigo esta incluido
También te puede interesar cómo hacer una ruleta :
¿Puedo hacer un spinner en Bash?
¡Por supuesto!
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 la cadena sp, envolviéndolo cuando llega al final. (i es la posición del carácter actual a mostrar y $ {# sp} es la longitud de la cadena sp).
La cadena / b se reemplaza por un carácter ''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 printf).
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 a la siguiente función al comienzo de cada iteración para actualizar el control de giro:
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
Tengo 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-$_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 quítatelo,
https://github.com/fearside/ProgressBar/
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
Usando las sugerencias enumeradas anteriormente, decidí implementar mi propia barra de progreso.
#!/usr/bin/env bash
main() {
for (( i = 0; i <= 100; i=$i + 20)); do
progress_bar "$i"
sleep 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 "$@"
Utilicé una respuesta de Creación de cadenas de caracteres repetidos en el script de shell para la repetición de caracteres. Tengo dos versiones de bash relativamente pequeñas para 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
}
Podría ser utilizado como:
preparebar 50 "#"
lo que significa preparar cadenas para la barra con 50 "#" caracteres, y después de eso:
progressbar 35 80
mostrará el número de caracteres "#" que corresponde 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 es todo en una 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 >&2
al final de cada comando printf.
usa el comando linux pv:
no sabe el tamaño si está en el medio de la secuencia, pero le da una velocidad y total y, a partir de ahí, puede determinar cuánto tiempo debería tardar y obtener retroalimentación para que sepa que no se ha colgado.
GNU tar tiene una opción útil que le da 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 puede ser obtenido por:
$ tar -c --checkpoint=.1000 /var
Primero ejecute el proceso en segundo plano, luego observe que se está ejecutando con frecuencia, imprima el patrón y nuevamente verifique si el estado se está ejecutando o no;
Utilizando bucle while para ver el estado del proceso con frecuencia.
use el comando pgrep o cualquier otro comando para ver y obtener el estado de ejecución de un proceso.
si usa pgrep, redirija la salida innecesaria a / dev / null según sea necesario.
Código:
sleep 12&
while pgrep sleep &> /dev/null;do echo -en "#";sleep 0.5;done
Este "#" se imprimirá hasta que finalice la suspensión, este método se utiliza para implementar la barra de progreso para el tiempo de progreso del programa.
También puede usar este método para los comandos de shell para analizar el tiempo de proceso como visual.
ERROR: este método pgrep no funciona en todas las situaciones, inesperadamente el otro proceso se estaba ejecutando con el mismo nombre, el bucle while no termina.
por lo tanto, al obtener el estado de ejecución del proceso, especifique su PID, utilizando el comando puede estar disponible con algunos comandos,
el comando ps a listará todo el proceso con id, necesita grep para encontrar el pid del proceso especificado
Puede implementar esto sobrescribiendo una línea. Use /r
para volver al principio de la línea sin escribir /n
en el terminal.
Escribe /n
cuando hayas terminado para avanzar en la línea.
Utilice echo -ne
para:
- no imprimir
/n
- 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 esto "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).
Quería hacer un seguimiento del progreso en función del número de líneas que un comando genera contra un número objetivo de líneas de una ejecución anterior:
#!/bin/bash
function lines {
local file=$1
local default=$2
if [[ -f $file ]]; then
wc -l $file | awk ''{print $1}'';
else
echo $default
fi
}
function bar {
local items=$1
local total=$2
local size=$3
percent=$(($items*$size/$total % $size))
left=$(($size-$percent))
chars=$(local s=$(printf "%${percent}s"); echo "${s// /=}")
echo -ne "[$chars>";
printf "%${left}s"
echo -ne '']/r''
}
function clearbar {
local size=$1
printf " %${size}s "
echo -ne "/r"
}
function progress {
local pid=$1
local total=$2
local file=$3
bar 0 100 50
while [[ "$(ps a | awk ''{print $1}'' | grep $pid)" ]]; do
bar $(lines $file 0) $total 50
sleep 1
done
clearbar 50
wait $pid
return $?
}
Entonces úsalo así:
target=$(lines build.log 1000)
(mvn clean install > build.log 2>&1) &
progress $! $target build.log
Da salida a una barra de progreso que se parece a esto:
[===============================================> ]
La barra crece a medida que el número de líneas de salida alcanza el objetivo. Si el número de líneas supera el objetivo, la barra comienza de nuevo (con suerte, el objetivo es bueno).
Por cierto: estoy usando bash en Mac OSX. mariascio este código en un spinner de mariascio .
Esta es una barra de progreso psicodélico para las secuencias de comandos bash de nExace. Se puede llamar desde la línea de comandos como ''./progressbar xy'' donde ''x'' es un tiempo en segundos y ''y'' es un mensaje asociado con esa parte del progreso.
La función de barra de progreso interna () en sí misma es buena independiente también si quiere que otras partes de su script controlen la barra de progreso. Por ejemplo, enviando ''barra de progreso 10 "Creando el árbol de directorios";'' mostrará:
[####### ] (10%) Creating directory tree
Por supuesto que será bien psicodélico aunque ...
#!/bin/bash
if [ "$#" -eq 0 ]; then echo "x is /"time in seconds/" and z is /"message/""; echo "Usage: progressbar x z"; exit; fi
progressbar() {
local loca=$1; local loca2=$2;
declare -a bgcolors; declare -a fgcolors;
for i in {40..46} {100..106}; do
bgcolors+=("$i")
done
for i in {30..36} {90..96}; do
fgcolors+=("$i")
done
local u=$(( 50 - loca ));
local y; local t;
local z; z=$(printf ''%*s'' "$u");
local w=$(( loca * 2 ));
local bouncer=".oO°Oo.";
for ((i=0;i<loca;i++)); do
t="${bouncer:((i%${#bouncer})):1}"
bgcolor="//E[${bgcolors[RANDOM % 14]}m //033[m"
y+="$bgcolor";
done
fgcolor="//E[${fgcolors[RANDOM % 14]}m"
echo -ne " $fgcolor$t$y$z$fgcolor$t //E[96m(//E[36m$w%//E[96m)//E[92m $fgcolor$loca2//033[m/r"
};
timeprogress() {
local loca="$1"; local loca2="$2";
loca=$(bc -l <<< scale=2/;"$loca/50")
for i in {1..50}; do
progressbar "$i" "$loca2";
sleep "$loca";
done
printf "/n"
};
timeprogress "$1" "$2"
#!/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