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?

¿Fue útil?

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:

  1. no imprime \ n y
  2. 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:

http://linux.die.net/man/1/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

 progress-bar.sh en acción

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

 introduce la descripción de la imagen aquí

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)

 introduce la descripción de la imagen aquí

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.

 introduce la descripción de la imagen aquí

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

https://github.com/extensionsapp/progre.sh

Cree un progreso del 40 por ciento: progreSh 40

 introduce la descripción de la imagen aquí

#!/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)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top