¿Cómo agregar una barra de progreso a un script de shell?


413

Al crear scripts en bash o cualquier otro shell en * NIX, mientras se ejecuta un comando que tomará más de unos segundos, se necesita una barra de progreso.

Por ejemplo, copiar un archivo grande, abrir un archivo tar grande.

¿De qué maneras recomienda agregar barras de progreso a los scripts de shell?


Consulte también stackoverflow.com/questions/12498304/… para ver ejemplos de la lógica de control (realice un trabajo en segundo plano y haga algo hasta que finalice).
tripleee

1
Hay un conjunto de requisitos que con frecuencia encontramos útiles al crear scripts. registro, visualización de progreso, colores, salidas elegantes, etc. Siempre he sentido que debería haber algún tipo de marco de programación simple. Finalmente he decidido implementar uno ya que no pude encontrar ninguno. Puede encontrar esto útil. Está en puro golpe, quiero decir Just Bash. github.com/SumuduLansakara/JustBash
Sumudu

¿No debería esto trasladarse a unix.stackexchange.com ?
Ethan

Respuestas:


685

Puede implementar esto sobrescribiendo una línea. Use \rpara regresar al comienzo de la línea sin escribir \nen la terminal.

Escriba \ncuando haya terminado para avanzar la línea.

Use echo -nepara:

  1. no imprimir \ny
  2. 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).


23
De acuerdo con la página de manual de echo (al menos en MacOS X) sh / bash usa su propio comando de eco incorporado que no acepta "-n" ... así que para lograr lo mismo debe poner \ r \ c al final de la cadena, en lugar de solo \ r
Justin Jenkins

51
La forma portátil de generar esto es usar en printflugar de echo.
Jens

99
para printf tendríamos que usar este formato: printf "#### (50%%)\r"no funcionaría con comillas simples y se debe escapar el signo de porcentaje.
nurettin

77
No entiendo esto: ¿aceptado y un montón de votos a favor para un truco de "Adivinaré cuánto tiempo durará esta operación en hardware desconocido"? pv es la respuesta correcta de la OMI (pero la barra también lo hará)
Stephen

19
La pregunta era "¿Cómo hago barras de progreso" con un ejemplo de copia de archivos. Me concentré en el problema de los "gráficos", no en el cálculo de qué tan avanzado está una operación de copia de archivos.
Mitch Haile

73

También te puede interesar cómo hacer una ruleta :

¿Puedo hacer una ruleta en Bash?

¡Por supuesto!

i=1
sp="/-\|"
echo -n ' '
while true
do
    printf "\b${sp:i++%${#sp}:1}"
done

Cada vez que el ciclo se repite, muestra el siguiente carácter en la cadena sp, envolviéndose 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 de 'retroceso'. Alternativamente, puedes jugar con \ r para volver al comienzo de la línea.

Si desea que se ralentice, coloque un comando de suspensión dentro del bucle (después de printf).

Un equivalente 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 la ruleta:

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

15
Versión mucho más corta, totalmente portátil *: while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*: sleeppuede requerir entradas en lugar de decimales)
Adam Katz

1
@Daenyth. Gracias. ¿Dónde deberíamos llamar al comando que necesitamos para ver si es progreso usando el código anterior?
goro

@ goro: en la some_work ...línea de arriba; Aquí puede encontrar una discusión más detallada que se basa en esta útil respuesta y el útil comentario de Adam Katz, con un enfoque en el cumplimiento de POSIX .
mklement0

@AdamKatz: Esa es una simplificación útil y portátil, pero para que coincida con el enfoque de Daenyth, la ruleta debe basarse en \blugar de \r, ya que de lo contrario solo funcionará al comienzo de una línea: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- o, si se muestra el cursor detrás de la ruleta no es deseado:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0

1
@kaushal: Ctrl + C lo detendrá manualmente. Si tiene un trabajo en segundo plano, puede almacenar su PID ( job=$!) y luego ejecutarlo while kill -0 $job 2>/dev/null;do …, por ejemplo:sleep 15 & job=$!; while kill -0 $job 2>/dev/null; do for s in / - \\ \|; do printf "\r$s"; sleep .1; done; done
Adam Katz

48

Algunas publicaciones han mostrado cómo mostrar el progreso del comando. Para calcularlo, necesitarás ver cuánto has progresado. En los sistemas BSD, algunos comandos, como dd (1), aceptan una SIGINFOseñal e informarán de su progreso. En los sistemas Linux, algunos comandos responderán de manera similar SIGUSR1. Si esta función está disponible, puede canalizar su entrada ddpara monitorear el número de bytes procesados.

Alternativamente, puede utilizar lsofpara 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 .


Esto es increíble, siempre me olvido de canalizar las cosas a través de pv :-) Creo que mi comando "stat" funciona un poco diferente, mi versión (Linux) de este script: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b
unhammer

Gran publicación, ¡siempre me encanta cuando awkestá en juego!
ShellFish

¡Esto es genial! ¡Gracias por el increíble guión!
thebeagle

47

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-$_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 enganche desde
https://github.com/fearside/ProgressBar/


¿Puedes explicar la línea bajo 1.2.1.1 por favor? ¿Está realizando una sustitución sed con las variables _fill y _empty? Estoy confundido.
Chirag

En lugar de usar sed, estoy usando bash "Reemplazo de subcadena" interno, ya que este es un trabajo fácil, prefiero usar las funciones internas de bash para este tipo de trabajo. El código también se ve mejor. :-) Verifique aquí tldp.org/LDP/abs/html/string-manipulation.html y busque el reemplazo de la subcadena.
Miedo

y $ {_ fill} se asigna como $ {_ done} número de espacios. Esto es hermoso. Gran trabajo hombre. Definitivamente voy a usar esto en todos mis scripts bash jaja
Chirag

¡Buen trabajo @fearside! Hice un pequeño ajuste para saltar cuando _progress no cambió desde el último valor, para mejorar la velocidad. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

Dulce. Cambiar el guión por rectángulo le da un aspecto más profesional:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI

44

usa el comando linux pv:

http://linux.die.net/man/1/pv

no sabe el tamaño si está en el medio de la transmisión, pero proporciona una velocidad y un total, y desde allí puede calcular cuánto tiempo debería tomar y obtener retroalimentación para que sepa que no se ha colgado.


32

Estaba buscando algo más sexy que la respuesta seleccionada, al igual que mi propio guión.

Avance

progress-bar.sh en acción

Fuente

Lo puse en githubprogress-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

1
No entiendo cómo se integra esto en algunos procesos donde no se conoce la duración del proceso. Cómo detener la barra de progreso si mi proceso finalizó antes, por ejemplo, para descomprimir un archivo.
ene

Creo que el uso debería serprogress-bar 100
jirarium

Progreso atractivo de hecho. ¿Cómo se puede vincular a una función que procesa acciones prolongadas en servidores remotos a través de ssh? Quiero decir, ¿cómo es posible medir el tiempo de una actualización (por ejemplo) en servidores remotos?
sin rostro el

1
@faceless no está en el alcance de este código que proporciona el tiempo y cuenta atrás
Édouard Lopez

1
@Fusion es un personaje unicode (U + 2587 BLOQUEO DE SIETE OCTAVAS INFERIOR) que debería ser seguro para el shell moderno. Pruébalo en tus envidias
Édouard Lopez

18

GNU tar tiene una opción útil que brinda la 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 la secuencia 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

¡+1 para el enfoque más simple! Si no ve puntos impresos, intente disminuir el número, por ejemplo --checkpoint=.10. También funciona muy bien cuando se extrae con tar -xz.
Noam Manos

13

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

13

No he visto nada similar y todas las funciones personalizadas aquí parecen centrarse solo en el renderizado, así que ... mi muy simple solución compatible con POSIX a continuación con explicaciones paso a paso porque esta pregunta no es trivial.

TL; DR

Renderizar la barra de progreso es muy fácil. Estimar cuánto debería renderizar es una cuestión diferente. Así es como se representa (anima) la barra de progreso: puede copiar y pegar este ejemplo en un archivo y ejecutarlo:

#!/bin/sh

BAR='####################'   # this is full bar, e.g. 20 chars

for i in {1..20}; do
    echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
    sleep .1                 # wait 100ms between "frames"
done
  • {1..20} - valores del 1 al 20
  • echo -n - imprimir sin nueva línea al final
  • echo -e - interpretar caracteres especiales durante la impresión
  • "\r" - retorno de carro, un carácter especial para volver al comienzo de la línea

Puede hacer que reproduzca cualquier contenido a cualquier velocidad, por lo que este método es muy universal, por ejemplo, se usa a menudo para visualizar "piratería" en películas tontas, no es broma.

Respuesta completa

El meollo del problema es cómo determinar el $ivalor, es decir, cuánto de la barra de progreso para mostrar. En el ejemplo anterior, simplemente dejé que se incremente en forbucle para ilustrar el principio, pero una aplicación de la vida real usaría un bucle infinito y calcularía la $ivariable en cada iteración. Para realizar dicho cálculo necesita los siguientes ingredientes:

  1. cuanto trabajo hay que hacer
  2. cuánto trabajo se ha hecho hasta ahora

En caso de cpque necesite el tamaño de un archivo fuente y el tamaño del archivo de destino:

#!/bin/sh

$src=/path/to/source/file
$tgt=/path/to/target/file

cp "$src" "$tgt" &                     # the & forks the `cp` process so the rest
                                       # of the code runs without waiting (async)

BAR='####################'

src_size=$(stat -c%s "$src")           # how much there is to do

while true; do
    tgt_size=$(stat -c%s "$tgt")       # how much has been done so far
    i=$(( $tgt_size * 20 / $src_size ))
    echo -ne "\r${BAR:0:$i}"
    if [ $tgt_size == $src_size ]; then
        echo ""                        # add a new line at the end
        break;                         # break the loop
    fi
    sleep .1
done
  • stat - verifique las estadísticas del archivo
  • -c - valor devuelto formateado
  • %s - tamaño total

En el caso de operaciones como el desempaquetado de archivos, calcular el tamaño de la fuente es un poco más difícil pero igual de fácil que obtener el tamaño de un archivo sin comprimir:

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
  • gzip -l - muestra información sobre el archivo zip
  • tail -n1 - trabajar con 1 línea desde abajo
  • tr -s ' ' - traducir múltiples espacios a uno (exprimirlos)
  • cut -d' ' -f3 - corte la tercera columna delimitada por espacios

Aquí está la carne del problema, sin embargo. Esta solución es cada vez menos general. Todos los cálculos del progreso real están estrechamente vinculados al dominio que está tratando de visualizar, es una operación de un solo archivo, una cuenta regresiva del temporizador, un número creciente de archivos en un directorio, operación en múltiples archivos, etc., por lo tanto, No se puede reutilizar. La única parte reutilizable es la representación de la barra de progreso. Para reutilizarlo, debe abstraerlo y guardarlo en un archivo (por ejemplo /usr/lib/progress_bar.sh), luego definir funciones que calculen los valores de entrada específicos de su dominio. Así es como podría verse un código generalizado (también hice la $BARdinámica porque la gente lo pedía, el resto ya debería estar claro):

#!/bin/sh

BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)

work_todo=$(get_work_todo)             # how much there is to do

while true; do
    work_done=$(get_work_done)         # how much has been done so far
    i=$(( $work_done * $BAR_length / $work_todo ))
    echo -ne "\r${BAR:0:$i}"
    if [ $work_done == $work_todo ]; then
        echo ""
        break;
    fi
    sleep .1
done
  • printf - un archivo incorporado para imprimir cosas en un formato dado
  • printf '%50s' - no imprima nada, rellene con 50 espacios
  • tr ' ' '#' - traduce cada espacio al signo hash

Y así es como lo usarías:

#!/bin/sh

src=/path/to/source/file
tgt=/path/to/target/file

function get_work_todo() {
    echo $(stat -c%s "$src")
}

function get_work_done() {
    [ -e "$tgt" ] &&                   # if target file exists
        echo $(stat -c%s "$tgt") ||    # echo its size, else
        echo 0                         # echo zero
}

cp "$src" "$tgt" &                     # copy in the background

source /usr/lib/progress_bar.sh        # execute the progress bar

Obviamente, puede envolverse en una función, reescribirse para trabajar con flujos entubados, reescribirse en otro idioma, sea cual sea su veneno.


1
Para aquellos que quieren las cosas más simples, acabo de hacer el mío con la primera respuesta de cprn. Es una barra de progreso muy simple en una función que usa una estúpida regla de proporcionalidad para dibujar la barra: pastebin.com/9imhRLYX
YCN-


9

Barra de progreso de estilo APT (no rompe la salida normal)

ingrese 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 estaba buscando personalmente era una barra de progreso elegante como lo ve APT.

Eché un vistazo al código fuente C de APT y decidí escribir mi propio equivalente para bash.

Esta barra de progreso permanecerá 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 desea 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 lo maneja bien).

Publicaré mi guión 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}"
}

7

Esto le permite visualizar que un comando aún 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 ciclo while infinito que se ejecuta en segundo plano y hace eco de un "." cada segundo. Esto se mostrará .en el shell. Ejecute el tarcomando o cualquier comando que desee. Cuando ese comando termine de ejecutarse, elimine el último trabajo que se ejecuta en segundo plano, que es el ciclo while infinito .


¿No podría comenzar otro trabajo en segundo plano durante la ejecución y potencialmente morir en lugar del ciclo de progreso?
Centimane

Creo que la idea es que pondrías esto en un script, por lo que esto solo atraparía una salida de ese script.
Iguananaut

1
Me encanta este comando, lo estoy usando en mis archivos. Estoy un poco incómodo ya que realmente no entiendo cómo funciona. La primera y la tercera línea son más fáciles de entender, pero todavía no estoy seguro. Sé que esta es una respuesta antigua, pero ¿hay alguna manera de obtener una explicación diferente dirigida a los novatos en la programación
Felipe

1
Esta es la ÚNICA respuesta verdadera, donde otros son solo barras de progreso de juguete Scripting 101 que no significan nada y no sirven para programas reales, únicos y no rastreables (casi TODOS). Gracias.
bekce

@Felipe, el ciclo while es un proceso en segundo plano. El $! en la primera captura captura la identificación del proceso de ese proceso en segundo plano y asegura que si el proceso actual / principal finaliza, el proceso en segundo plano también muere y no queda colgado. La instrucción kill simplemente finaliza el proceso en segundo plano cuando finaliza su comando o comandos largos.
floydn

7

Así es como podría verse

Subiendo un archivo

[##################################################] 100% (137921 / 137921 bytes)

A la espera de un trabajo para completar

[#########################                         ] 50% (15 / 30 seconds)

Función simple que lo 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 (( $__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 redibujamos 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_sizey el valor actual es suministrado por 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"

Función ordenada y simple. ¡Muchas gracias!
Andreas Kraft el

¡Esto es lo que estoy buscando! Muchas gracias :)
wajdi_jurry

4

La mayoría de los comandos de Unix no le darán el tipo de retroalimentación directa de la que puede hacer esto. Algunos le darán salida en stdout o stderr que puede usar.

Para algo como tar, podría usar el modificador -v y canalizar la salida a un programa que actualice una pequeña animación para cada línea que lea. A medida que el tar escribe una lista de archivos, el programa puede actualizar la animación. Para completar un porcentaje, debe conocer la cantidad de archivos y contar las líneas.

cp no da este tipo de salida por lo que yo sé. Para monitorear el progreso de cp, debería monitorear los archivos de origen y de destino y observar el tamaño del destino. Puede escribir un pequeño programa en C utilizando la llamada al sistema stat (2) para obtener el tamaño del archivo. Esto leería el tamaño de la fuente, luego sondearía el archivo de destino y actualizaría una barra de% completado en función del tamaño del archivo escrito hasta la fecha.


4

Mi solución muestra el porcentaje del tarball que actualmente se está descomprimiendo y escribiendo. Lo uso 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 --listpara obtener el tamaño total sin comprimir del 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 2 GB, esto da aproximadamente 10 MB por bloque. Si eso es demasiado grande, puede dividir el BLOCKING_FACTOR por 10 o 100, pero es más difícil imprimir una salida bonita en términos de porcentaje.

Suponiendo que está usando Bash, 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}
}

Buena solución, pero ¿cómo lo haces cuando quieres comprimir un directorio?
Samir Sadek el

4

En primer lugar, la barra no es el único medidor de progreso de tubería. El otro (quizás 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 funcionan con archivos dados en argumentos, como por ejemplo, copiar archivo1 archivo2, es usar la sustitución del proceso :

$ copy <(bar file1) file2
$ copy <(pv file1) file2

La sustitución de procesos es una cosa mágica que crea archivos temporales de quince tubos / dev / fd / y conecta stdout del proceso ejecutado (dentro del paréntesis) a través de este tubo y la copia lo ve como un archivo ordinario (con una excepción, solo puede leerlo hacia adelante).

Actualizar:

El comando bar también permite copiar. Después del hombre bar:

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. Y usa el propio programa cp.


3

Prefiero usar el diálogo con el parámetro --gauge . Se usa muy a menudo en 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 permisos chmod u + x) 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"

Entonces puedo poner:

Wait "34 min" "warm up the oven"

o

Wait "dec 31" "happy new year"


2

para mí, lo más fácil de usar y lo mejor hasta ahora es el comando pvo barcomo algún tipo ya escribió

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 pvusted puede hacerlo así:

dd if="$input_drive_path" | pv | dd of="$output_file_path"

y el progreso va directamente a STDOUTesto:

    7.46GB 0:33:40 [3.78MB/s] [  <=>                                            ]

una vez hecho, aparece el resumen

    15654912+0 records in
    15654912+0 records out
    8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s

¿Puede usar pvo barvisualizar el progreso de diferentes procesos, por ejemplo, cuenta regresiva del temporizador, posición en un archivo de texto, instalación de su aplicación, configuración del tiempo de ejecución, etc.?
cprn

2

Muchas respuestas describen 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 myFoldergenerará cada nombre de archivo a medida que lo comprime), entonces existe una solución más simple, rápida, indolora y personalizable.

Instala 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 con más opciones:

$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l

Como beneficio adicional, también puede usar tqdmpara envolver iterables en código python.

https://github.com/tqdm/tqdm/blob/master/README.rst#module


No creo que su ejemplo con "más opciones" funcione. Parece pasar el tqdmSTDOUT a wc -ltravés de una tubería. Probablemente quieras escapar de eso.
cprn

1
@cprn tqdmmostrará el progreso STDERRmientras se canaliza su entrada STDINa STDOUT. En este caso wc -lrecibiría la misma entrada como si tqdmno estuviera incluida.
casper.dcl

Ah, tiene sentido ahora. Gracias por la explicación.
cprn

2

Basado en el trabajo de Edouard Lopez, creé una barra de progreso que se ajusta al tamaño de la pantalla, sea lo que sea. Echale un vistazo.

ingrese la descripción de la imagen aquí

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



1

Esto solo es aplicable usando gnome zenity. Zenity proporciona una excelente interfaz nativa para bash scripts: https://help.gnome.org/users/zenity/stable/

Del 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

1

Usé una respuesta de Crear una cadena de caracteres repetidos en el script de shell para la repetición de caracteres. Tengo dos versiones bash relativamente pequeñas para los scripts que necesitan mostrar la barra de progreso (por ejemplo, un bucle que atraviesa muchos archivos, pero no es útil para archivos tar grandes o 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 "#"

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á:

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 que el anterior):

progressbar 35 80 50

Si necesita barra de progreso en stderr, simplemente agregue >&2al final de cada comando printf.


1

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;

Uno puede usar banderas / variables dentro del ciclo while para verificar y mostrar el valor / extensión del progreso.


1

Utilizando 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 "$@"

1
¡Agradable! para que funcione, tuve que cambiar la línea percent_none="$(( 100 - "$percent_done" ))"apercent_none="$(( 100 - $percent_done))"
sergio

0

Hice una versión de shell pura para un sistema embebido aprovechando:

  • 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)', generará un resumen de la velocidad de procesamiento y la cantidad transferida.

  • dd en segundo plano y luego consultarlo regularmente para obtener actualizaciones, y generar marcas de hash como solían hacerlo los clientes ftp de la vieja escuela.

  • Usando / dev / stdout como el 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 'hash' FTP de la vieja escuela donde solo obtendría una marca hash por cada X bytes.

Este no es un código de calidad de producción, pero ya se entiende la idea. Pienso que es lindo.

Para lo que vale, el recuento de bytes real puede no reflejarse correctamente en el número de hashes; puede tener uno más o menos dependiendo de los problemas de redondeo. No use esto como parte de un guión de prueba, es simplemente atractivo. 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 proporcionados al final. Debería funcionar con cualquier cosa que haya emitido datos. Asegúrese de usar / dev / stdout para programas que no sean compatibles con 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

Idea decente, siempre que tenga el tamaño del archivo con anticipación, puede proporcionar un valor agregado que pv de esta manera, pero la señalización ciega pidof ddes aterradora.

Intenté llamar eso con '# Mi manejo de PID es tonto'
sintetizadorpatel

Usted tal vez puede capturar $!desde ddy esperar en [[ -e /proc/${DD_PID} ]].

0

En caso de que tenga que mostrar una barra de progreso temporal (al conocer 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 haya guardado 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 del tamaño de 50caracteres y "corriendo" por 10segundos.


0

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-$_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'

0
#!/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 digamos 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

0
#!/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 pones 1, monitorearás la entrada estándar ... con 2 la salida estándar (pero debes modificar la fuente para establecer "tot" al tamaño de archivo de salida proyectado)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.