bash shell unix progress

Usar BASH para mostrar un indicador de progreso(trabajo)



shell unix (11)

@ Los comentarios de DavidD sobre la respuesta de Pez Cuckows, este es un ejemplo de cómo se puede capturar el resultado de la barra de progreso en un script y aún ver el control giratorio en la pantalla:

#!/usr/bin/env bash ############################################################################# ########################################################################### ### ### Modified/Rewritten by A.M.Danischewski (c) 2015 v1.1 ### Issues: If you find any issues emai1 me at my <first name> dot ### <my last name> at gmail dot com. ### ### Based on scripts posted by Pez Cuckow, William Pursell at: ### http://stackoverflow.com/questions/12498304/using-bash-to-display-/ ### a-progress-working-indicator ### ### This program runs a program passed in and outputs a timing of the ### command and it exec''s a new fd for stdout so you can assign a ### variable the output of what was being run. ### ### This is a very new rough draft but could be expanded. ### ### This program is free software: you can redistribute it and/or modify ### it under the terms of the GNU General Public License as published by ### the Free Software Foundation, either version 3 of the License, or ### (at your option) any later version. ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program. If not, see <http://www.gnu.org/licenses/>. ########################################################################### ############################################################################# declare CMD="${1}" shift ## Clip the first value of the $@, the rest are the options. declare CMD_OPTIONS="$@" declare CMD_OUTPUT="" declare TMP_OUTPUT="/tmp/_${0##*/}_$$_$(date +%Y%m%d%H%M%S%N)" declare -r SPIN_DELAY="0.1" declare -i PID= function usage() { cat <<EOF Description: ${0##*/} This program runs a program passed in and outputs a timing of the command and it exec''s a new fd for stdout so you can assign a variable the output of what was being run. Usage: ${0##*/} <command> [command options] E.g. >$ ${0##*/} sleep 5 /&/& echo "hello" /| figlet Running: sleep 5 && echo hello | figlet, PID 2587:/ real 0m5.003s user 0m0.000s sys 0m0.002s _ _ _ | |__ ___| | | ___ | ''_ / / _ / | |/ _ / | | | | __/ | | (_) | |_| |_|/___|_|_|/___/ Done.. >$ var=/$(${0##*/} sleep 5 /&/& echo hi) Running: sleep 5 && echo hi, PID 32229:- real 0m5.003s user 0m0.000s sys 0m0.001s Done.. >$ echo /$var hi EOF } function spin_wait() { local -a spin spin[0]="-" spin[1]="//" spin[2]="|" spin[3]="/" echo -en "Running: ${CMD} ${CMD_OPTIONS}, PID ${PID}: " >&3 while kill -0 ${PID} 2>/dev/random; do for i in "${spin[@]}"; do echo -ne "/b$i" >&3 sleep ${SPIN_DELAY} done done } function run_cmd() { exec 3>$(tty) eval "time ${CMD} ${CMD_OPTIONS}" 2>>"${TMP_OUTPUT}" | tee "${TMP_OUTPUT}" & PID=$! # Set global PID to process id of the command we just ran. spin_wait echo -en "/n$(< "${TMP_OUTPUT}")/n" >&3 echo -en "Done../n" >&3 rm "${TMP_OUTPUT}" exec 3>&- } if [[ -z "${CMD}" || "${CMD}" =~ ^-. ]]; then usage | more && exit 0 else run_cmd fi exit 0

Usando un script bash only, ¿cómo puedes proporcionar un indicador de progreso bash?

Entonces puedo ejecutar un comando de forma bash, y mientras se está ejecutando ese comando, que el usuario sepa que algo está sucediendo.


A partir de here se hace referencia a una buena función de centrifugado (con una ligera modificación), que también ayudará al cursor a permanecer en la posición original.

spinner() { local pid=$! local delay=0.75 local spinstr=''|/-/' while [ "$(ps a | awk ''{print $1}'' | grep $pid)" ]; do local temp=${spinstr#?} printf " [%c] " "$spinstr" local spinstr=$temp${spinstr%"$temp"} sleep $delay printf "/b/b/b/b/b/b" done printf " /b/b/b/b" }

con uso:

(a_long_running_task) & spinner



Aquí está mi intento. Soy nuevo en los scripts bash, así que parte de este código puede ser terrible :)

Ejemplo de salida:

El código:

progressBarWidth=20 # Function to draw progress bar progressBar () { # Calculate number of fill/empty slots in the bar progress=$(echo "$progressBarWidth/$taskCount*$tasksDone" | bc -l) fill=$(printf "%.0f/n" $progress) if [ $fill -gt $progressBarWidth ]; then fill=$progressBarWidth fi empty=$(($fill-$progressBarWidth)) # Percentage Calculation percent=$(echo "100/$taskCount*$tasksDone" | bc -l) percent=$(printf "%0.2f/n" $percent) if [ $(echo "$percent>100" | bc) -gt 0 ]; then percent="100.00" fi # Output to screen printf "/r[" printf "%${fill}s" '''' | tr '' '' ▉ printf "%${empty}s" '''' | tr '' '' ░ printf "] $percent%% - $text " } ## Collect task count taskCount=33 tasksDone=0 while [ $tasksDone -le $taskCount ]; do # Do your task (( tasksDone += 1 )) # Add some friendly output text=$(echo "somefile-$tasksDone.dat") # Draw the progress bar progressBar $taskCount $taskDone $text sleep 0.01 done echo

Puede ver la fuente aquí: https://gist.github.com/F1LT3R/fa7f102b08a514f2c535


Aquí hay un ejemplo de un "indicador de actividad" para una prueba de velocidad de conexión a Internet a través del comando linux "speedtest-cli":

printf ''/n/tInternet speed test: '' # http://.com/questions/12498304/using-bash-to-display-a-progress-working-indicator spin[0]="-" spin[1]="//" spin[2]="|" spin[3]="/" # http://.com/questions/20165057/executing-bash-loop-while-command-is-running speedtest > .st.txt & ## & : continue running script pid=$! ## PID of last command # If this script is killed, kill ''speedtest'': trap "kill $pid 2> /dev/null" EXIT # While ''speedtest'' is running: while kill -0 $pid 2> /dev/null; do for i in "${spin[@]}" do echo -ne "/b$i" sleep 0.1 done done # Disable the trap on a normal exit: trap - EXIT printf "/n/t " grep Download: .st.txt printf "/t " grep Upload: .st.txt echo '''' rm -f st.txt

Actualización - ejemplo:


Aquí un simple onliner, que uso:

while true; do for X in ''-'' ''/'' ''|'' ''/'; do echo -en "/b$X"; sleep 0.1; done; done


Barra de progreso psicodélica para scripts de bash. Llamar por línea de comando como ''./progressbar xy'' donde ''x'' es un tiempo en segundos e ''y'' es un mensaje para mostrar. La función interna progressbar () también funciona de forma independiente y toma ''x'' como un porcentaje e ''y'' como un mensaje.

#!/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 echo -e "/n" }; timeprogress "$1" "$2"


En este ejemplo usando SCP, estoy demostrando cómo agarrar el id del proceso (pid) y luego hacer algo mientras el proceso se está ejecutando.

Esto muestra un icono de spinnng simple.

/usr/bin/scp [email protected]:file somewhere 2>/dev/null & pid=$! # Process Id of the previous running command spin[0]="-" spin[1]="//" spin[2]="|" spin[3]="/" echo -n "[copying] ${spin[0]}" while [ kill -0 $pid ] do for i in "${spin[@]}" do echo -ne "/b$i" sleep 0.1 done done

La solución de William Pursell

/usr/bin/scp [email protected]:file somewhere 2>/dev/null & pid=$! # Process Id of the previous running command spin=''-/|/'' i=0 while kill -0 $pid 2>/dev/null do i=$(( (i+1) %4 )) printf "/r${spin:$i:1}" sleep .1 done


Extendí la respuesta de de en su respuesta al mostrar un mensaje de información variable después de la flecha giratoria:

#!/usr/bin/env bash function spinner() { local info="$1" local pid=$! local delay=0.75 local spinstr=''|/-/' while kill -0 $pid 2> /dev/null; do local temp=${spinstr#?} printf " [%c] $info" "$spinstr" local spinstr=$temp${spinstr%"$temp"} sleep $delay local reset="/b/b/b/b/b/b" for ((i=1; i<=$(echo $info | wc -c); i++)); do reset+="/b" done printf $reset done printf " /b/b/b/b" } # usage: (a_long_running_task) & spinner "performing long running task..."

No me gusta que si la salida stdout con un spinner se redirige a un archivo, less muestra ^H para cada retroceso en lugar de evitarlos en una salida de archivo. ¿Es posible con un spinner fácil como este?


Si tiene una forma de estimar el porcentaje realizado, como la cantidad actual de archivos procesados ​​y el número total, puede hacer un medidor de progreso lineal simple con un poco de matemática y suposiciones sobre el ancho de la pantalla.

count=0 total=34 pstr="[=======================================================================]" while [ $count -lt $total ]; do sleep 0.5 # this is work count=$(( $count + 1 )) pd=$(( $count * 73 / $total )) printf "/r%3d.%1d%% %.${pd}s" $(( $count * 100 / $total )) $(( ($count * 1000 / $total) % 10 )) $pstr done

O en lugar de un medidor lineal, puedes calcular el tiempo restante. Es tan preciso como otras cosas similares.

count=0 total=34 start=`date +%s` while [ $count -lt $total ]; do sleep 0.5 # this is work cur=`date +%s` count=$(( $count + 1 )) pd=$(( $count * 73 / $total )) runtime=$(( $cur-$start )) estremain=$(( ($runtime * $total / $count)-$runtime )) printf "/r%d.%d%% complete ($count of $total) - est %d:%0.2d remaining/e[K" $(( $count*100/$total )) $(( ($count*1000/$total)%10)) $(( $estremain/60 )) $(( $estremain%60 )) done printf "/ndone/n"


Esta es una técnica bastante fácil:
(simplemente reemplace sleep 20 con el comando que quiera indicar que se está ejecutando)

#!/bin/bash sleep 20 & PID=$! #simulate a long process echo "THIS MAY TAKE A WHILE, PLEASE BE PATIENT WHILE ______ IS RUNNING..." printf "[" # While process is running... while kill -0 $PID 2> /dev/null; do printf "▓" sleep 1 done printf "] done!"

La salida se ve así:

> THIS MAY TAKE A WHILE, PLEASE BE PATIENT WHILE ______ IS RUNNING... > [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] done!

Agrega un (densidad alta punteada) cada segundo hasta que el proceso se completa.