Script de bash para calcular el tiempo transcurrido


118

Estoy escribiendo un script en bash para calcular el tiempo transcurrido para la ejecución de mis comandos, considere:

STARTTIME=$(date +%s)
#command block that takes time to complete...
#........
ENDTIME=$(date +%s)
echo "It takes $($ENDTIME - $STARTTIME) seconds to complete this task..."

Supongo que mi lógica es correcta, sin embargo, termino con la siguiente impresión:

"Se necesitan unos segundos para completar esta tarea ..."

¿Hay algún problema con mi evaluación de cadenas?

Creo que las variables de bash no están tipificadas, me encantaría que, sin embargo, hubiera un método de "cadena a entero" en bash.

Respuestas:


83

Ya sea $(())o $[]va a trabajar para calcular el resultado de una operación aritmética. Estás usando $()que es simplemente tomar la cadena y evaluarla como un comando. Es una distinción un poco sutil. Espero que esto ayude.

Como Tink señaló en los comentarios sobre esta respuesta, $[]está en desuso y $(())debería favorecerse.


7
Es posible que desee intercambiar esos dos, ya que la página de manual de bash 4.x indica que $ [] está obsoleto y se eliminará en versiones futuras.
tink

2
Gracias, no lo sabía.
OmnipotentEntity

157

Me parece muy limpio usar la variable interna "$ SECONDS"

SECONDS=0 ; sleep 10 ; echo $SECONDS


10
Solo el éxito =)
Lon Kaut

1
Necesitas éxito, usa el tuyo
Gromish

3
$SECONDSde hecho funciona para / bin / bash. No funciona para / bin / dash, el shell predeterminado en Debian y Ubuntu.
Cameron Taggart

2
La desventaja de esta solución es que solo mide segundos enteros, es decir, no se puede utilizar si necesita una precisión inferior a un segundo.
Czechnology

@Czechnology sí, si usa sleep 0.5en arriba, el resultado es a veces 0, a veces 1 (al menos por Bash 5.0.3).
jarno

52

Está intentando ejecutar el número en el ENDTIMEcomo un comando. También debería ver un error como 1370306857: command not found. En su lugar, use la expansión aritmética :

echo "It takes $(($ENDTIME - $STARTTIME)) seconds to complete this task..."

También puede guardar los comandos en un script separado commands.shy usar el comando time:

time commands.sh

28

Puede usar la timepalabra clave de Bash aquí con una cadena de formato adecuada

TIMEFORMAT='It takes %R seconds to complete this task...'
time {
    #command block that takes time to complete...
    #........
 }

Esto es lo que diceTIMEFORMAT la referencia :

El valor de este parámetro se utiliza como una cadena de formato que especifica cómo se time debe mostrar la información de tiempo para las canalizaciones con el prefijo de la palabra reservada. El carácter " %" introduce una secuencia de escape que se expande a un valor de tiempo u otra información. Las secuencias de escape y sus significados son los siguientes; las llaves denotan porciones opcionales.

%%

    A literal ‘%’.
%[p][l]R

    The elapsed time in seconds.
%[p][l]U

    The number of CPU seconds spent in user mode.
%[p][l]S

    The number of CPU seconds spent in system mode.
%P

    The CPU percentage, computed as (%U + %S) / %R. 

La p opcional es un dígito que especifica la precisión, el número de dígitos fraccionarios después de un punto decimal. Un valor de 0 hace que no se emita ningún punto decimal o fracción. Se pueden especificar como máximo tres lugares después del punto decimal; los valores de p mayores que 3 se cambian a 3. Si no se especifica p , se usa el valor 3.

El opcional lespecifica un formato más largo, incluidos los minutos, de la forma MMmSS.FFs. El valor de p determina si se incluye o no la fracción.

Si esta variable no se establece, Bash actúa como si tuviera el valor

$'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'

Si el valor es nulo, no se muestra información de tiempo. Se agrega una nueva línea al final cuando se muestra la cadena de formato.


10

Para números más grandes, es posible que deseemos imprimir en un formato más legible. El siguiente ejemplo hace lo mismo que otros, pero también se imprime en formato "humano":

secs_to_human() {
    if [[ -z ${1} || ${1} -lt 60 ]] ;then
        min=0 ; secs="${1}"
    else
        time_mins=$(echo "scale=2; ${1}/60" | bc)
        min=$(echo ${time_mins} | cut -d'.' -f1)
        secs="0.$(echo ${time_mins} | cut -d'.' -f2)"
        secs=$(echo ${secs}*60|bc|awk '{print int($1+0.5)}')
    fi
    echo "Time Elapsed : ${min} minutes and ${secs} seconds."
}

Prueba simple:

secs_to_human "300"
secs_to_human "305"
secs_to_human "59"
secs_to_human "60"
secs_to_human "660"
secs_to_human "3000"

Salida:

Time Elapsed : 5 minutes and 0 seconds.
Time Elapsed : 5 minutes and 5 seconds.
Time Elapsed : 0 minutes and 59 seconds.
Time Elapsed : 1 minutes and 0 seconds.
Time Elapsed : 11 minutes and 0 seconds.
Time Elapsed : 50 minutes and 0 seconds.

Para usar en un script como se describe en otras publicaciones (capturar el punto de inicio y luego llamar a la función con la hora de finalización:

start=$(date +%s)
# << performs some task here >>
secs_to_human "$(($(date +%s) - ${start}))"

9

Prueba el siguiente código:

start=$(date +'%s') && sleep 5 && echo "It took $(($(date +'%s') - $start)) seconds"

5

Esta es una alternativa de una sola línea a la función de Mike Q:

secs_to_human() {
    echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}

¡Agradable! Por lo general, soy muy detallado con mi código bash, esto es genial.
Mike Q

Combinar esto con SECONDSla respuesta de Lon Kaut y tener en cuenta que $ / $ {} es innecesario en variables aritméticas hace que el código sea tan corto que incluso podría usarse en línea:echo "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
ssc

2

intente usar el tiempo con la opción de segundos transcurridos:

/usr/bin/time -f%e sleep 1 bajo bash.

o \time -f%e sleep 1en bash interactivo.

ver la página del manual de tiempo:

Los usuarios del shell bash deben utilizar una ruta explícita para ejecutar el comando de tiempo externo y no la variante incorporada del shell. En el sistema donde la hora está instalada en / usr / bin, el primer ejemplo se convertiría en / usr / bin / time wc / etc / hosts

y

FORMATTING THE OUTPUT
...
    %      A literal '%'.
    e      Elapsed  real  (wall  clock) time used by the process, in
                 seconds.

1
/bin/timeno va a funcionar aquí: OP menciona un bloque . Entonces realmente necesitamos la palabra clave timeaquí.
gniourf_gniourf

-3
start=$(date +%Y%m%d%H%M%S);
for x in {1..5};
do echo $x;
sleep 1; done;
end=$(date +%Y%m%d%H%M%S);
elapsed=$(($end-$start));
ftime=$(for((i=1;i<=$((${#end}-${#elapsed}));i++));
        do echo -n "-";
        done;
        echo ${elapsed});
echo -e "Start  : ${start}\nStop   : ${end}\nElapsed: ${ftime}"

Start  : 20171108005304
Stop   : 20171108005310
Elapsed: -------------6

-3
    #!/bin/bash

    time_elapsed(){
    appstop=$1; appstart=$2

    ss_strt=${appstart:12:2} ;ss_stop=${appstop:12:2}
    mm_strt=${appstart:10:2} ;mm_stop=${appstop:10:2}
     hh_strt=${appstart:8:2} ; hh_stop=${appstop:8:2}
     dd_strt=${appstart:6:2} ; dd_stop=${appstop:6:2}
     mh_strt=${appstart:4:2} ; mh_stop=${appstop:4:2}
     yy_strt=${appstart:0:4} ; yy_stop=${appstop:0:4}

    if [ "${ss_stop}" -lt "${ss_strt}" ]; then ss_stop=$((ss_stop+60)); mm_stop=$((mm_stop-1)); fi
    if [ "${mm_stop}" -lt "0" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
    if [ "${mm_stop}" -lt "${mm_strt}" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
    if [ "${hh_stop}" -lt "0" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi
    if [ "${hh_stop}" -lt "${hh_strt}" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi

    if [ "${dd_stop}" -lt "0" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi
    if [ "${dd_stop}" -lt "${dd_strt}" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi

    if [ "${mh_stop}" -lt "0" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi
    if [ "${mh_stop}" -lt "${mh_strt}" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi

    ss_espd=$((10#${ss_stop}-10#${ss_strt})); if [ "${#ss_espd}" -le "1" ]; then ss_espd=$(for((i=1;i<=$((${#ss_stop}-${#ss_espd}));i++)); do echo -n "0"; done; echo ${ss_espd}); fi
    mm_espd=$((10#${mm_stop}-10#${mm_strt})); if [ "${#mm_espd}" -le "1" ]; then mm_espd=$(for((i=1;i<=$((${#mm_stop}-${#mm_espd}));i++)); do echo -n "0"; done; echo ${mm_espd}); fi
    hh_espd=$((10#${hh_stop}-10#${hh_strt})); if [ "${#hh_espd}" -le "1" ]; then hh_espd=$(for((i=1;i<=$((${#hh_stop}-${#hh_espd}));i++)); do echo -n "0"; done; echo ${hh_espd}); fi
    dd_espd=$((10#${dd_stop}-10#${dd_strt})); if [ "${#dd_espd}" -le "1" ]; then dd_espd=$(for((i=1;i<=$((${#dd_stop}-${#dd_espd}));i++)); do echo -n "0"; done; echo ${dd_espd}); fi
    mh_espd=$((10#${mh_stop}-10#${mh_strt})); if [ "${#mh_espd}" -le "1" ]; then mh_espd=$(for((i=1;i<=$((${#mh_stop}-${#mh_espd}));i++)); do echo -n "0"; done; echo ${mh_espd}); fi
    yy_espd=$((10#${yy_stop}-10#${yy_strt})); if [ "${#yy_espd}" -le "1" ]; then yy_espd=$(for((i=1;i<=$((${#yy_stop}-${#yy_espd}));i++)); do echo -n "0"; done; echo ${yy_espd}); fi

    echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}"
    #return $(echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}")
    }

    mh_days(){
    mh_stop=$1; yy_stop=$2; #also checks if it's leap year or not

    case $mh_stop in
     [1,3,5,7,8,10,12]) mh_stop=31
     ;;
     2) (( !(yy_stop % 4) && (yy_stop % 100 || !(yy_stop % 400) ) )) && mh_stop=29 || mh_stop=28
     ;;
     [4,6,9,11]) mh_stop=30
     ;;
    esac

    return ${mh_stop}
    }

    appstart=$(date +%Y%m%d%H%M%S); read -p "Wait some time, then press nay-key..." key; appstop=$(date +%Y%m%d%H%M%S); elapsed=$(time_elapsed $appstop $appstart); echo -e "Start...: ${appstart:0:4}-${appstart:4:2}-${appstart:6:2} ${appstart:8:2}:${appstart:10:2}:${appstart:12:2}\nStop....: ${appstop:0:4}-${appstop:4:2}-${appstop:6:2} ${appstop:8:2}:${appstop:10:2}:${appstop:12:2}\n$(printf '%0.1s' "="{1..30})\nElapsed.: ${elapsed}"

    exit 0


-------------------------------------------- return
Wait some time, then press nay-key...
Start...: 2017-11-09 03:22:17
Stop....: 2017-11-09 03:22:18
==============================
Elapsed.: 0000-00-00 00:00:01
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.