Saltar al contenido

¿Cómo calcular el tiempo transcurrido en el script bash?

Solución:

Bash tiene un útil SECONDS variable incorporada que rastrea el número de segundos que han pasado desde que se inició el shell. Esta variable conserva sus propiedades cuando se asigna a, y el valor devuelto después de la asignación es el número de segundos desde la asignación más el valor asignado.

Por lo tanto, puede configurar SECONDS a 0 antes de iniciar el evento cronometrado, simplemente lea SECONDS después del evento, y haga la aritmética de tiempo antes de mostrar.

SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."

Como esta solución no depende de date +%s (que es una extensión GNU), es portátil para todos los sistemas compatibles con Bash.

Segundos

Para medir el tiempo transcurrido (en segundos) necesitamos:

  • un número entero que representa el recuento de segundos transcurridos y
  • una forma de convertir dicho número entero a un formato utilizable.

Un valor entero de segundos transcurridos:

  • Hay dos formas internas de bash para encontrar un valor entero para el número de segundos transcurridos:

    1. Bash variable SECONDS (si SECONDS no está configurada, pierde su propiedad especial).

      • Estableciendo el valor de SEGUNDOS en 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
        
      • Almacenar el valor de la variable SECONDS al principio:

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
        
    2. Opción Bash printf %(datefmt)T:

      a="$(TZ=UTC0 printf '%(%s)Tn' '-1')"    ### `-1`  is the current time
      sleep 1                                  ### Process to execute
      elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)Tn' '-1') - a ))
      

Convierta dicho número entero a un formato utilizable

El bash interno printf puede hacer eso directamente:

$ TZ=UTC0 printf '%(%H:%M:%S)Tn' 12345
03:25:45

similar

$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)Tn' "$elapsedseconds"
00:12:34

pero esto fallará por duraciones de más de 24 horas, ya que en realidad imprimimos una hora de reloj de pared, no realmente una duración:

$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)Tn' "$elapsedseconds"
06:12:24

Para los amantes de los detalles, de bash-hackers.org:

%(FORMAT)T genera la cadena de fecha y hora resultante de usar FORMAT como una cadena de formato para strftime(3). El argumento asociado es el número de segundos desde Epoch, o -1 (tiempo actual) o -2 (tiempo de inicio del shell). Si no se proporciona ningún argumento correspondiente, la hora actual se utiliza por defecto.

Entonces es posible que desee simplemente llamar textifyDuration $elpasedseconds dónde textifyDuration es otra implementación de la impresión de duración:

textifyDuration() {
   local duration=$1
   local shiff=$duration
   local secs=$((shiff % 60));  shiff=$((shiff / 60));
   local mins=$((shiff % 60));  shiff=$((shiff / 60));
   local hours=$shiff
   local splur; if [ $secs  -eq 1 ]; then splur=""; else splur="s"; fi
   local mplur; if [ $mins  -eq 1 ]; then mplur=""; else mplur="s"; fi
   local hplur; if [ $hours -eq 1 ]; then hplur=""; else hplur="s"; fi
   if [[ $hours -gt 0 ]]; then
      txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
   elif [[ $mins -gt 0 ]]; then
      txt="$mins minute$mplur, $secs second$splur"
   else
      txt="$secs second$splur"
   fi
   echo "$txt (from $duration seconds)"
}

Fecha GNU.

Para obtener el tiempo formateado, deberíamos usar una herramienta externa (fecha GNU) de varias maneras para obtener hasta casi un año de duración e incluir nanosegundos.

Matemáticas dentro de la fecha.

No hay necesidad de aritmética externa, hágalo todo en un solo paso adentro date:

date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"

Si, hay un 0 cero en la cadena de comando. Es necesario.

Eso es asumiendo que podrías cambiar el date +"%T" comando a un date +"%s" comando para que los valores se almacenen (impriman) en segundos.

Tenga en cuenta que el comando se limita a:

  • Valores positivos de $StartDate y $FinalDate segundos.
  • El valor en $FinalDate es más grande (más tarde en el tiempo) que $StartDate.
  • Diferencia horaria inferior a 24 horas.
  • Acepta un formato de salida con Horas, Minutos y Segundos. Muy fácil de cambiar.
  • Es aceptable utilizar -u horas UTC. Para evitar “DST” y correcciones de la hora local.

Si tu debe utilizar el 10:33:56 cadena, bueno, conviértala en segundos,
Además, la palabra segundos se podría abreviar como sec:

string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"

Tenga en cuenta que la conversión de tiempo en segundos (como se presentó arriba) es relativa al inicio de “este” día (Hoy).


El concepto podría extenderse a nanosegundos, así:

string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"

Si se requiere calcular diferencias de tiempo más largas (hasta 364 días), debemos usar el inicio de (algún) año como referencia y el valor de formato %j (el número de día del año):

Similar a:

string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"

Output:
026 days 00:02:14.340003400

Lamentablemente, en este caso, debemos restar manualmente 1 UNO del número de días. El comando de fecha ve el primer día del año como 1. No es tan difícil …

a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"

El uso de una gran cantidad de segundos es válido y está documentado aquí:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Fecha de Busybox

Una herramienta utilizada en dispositivos más pequeños (un ejecutable muy pequeño para instalar): Busybox.

Haga un enlace a busybox llamado fecha:

$ ln -s /bin/busybox date

Úselo entonces llamando a esto date (colóquelo en un directorio incluido PATH).

O crea un alias como:

$ alias date="busybox date"

Busybox date tiene una buena opción: -D para recibir el formato de la hora de entrada. Eso abre muchos formatos para usar como tiempo. Usando la opción -D podemos convertir la hora 10:33:56 directamente:

date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"

Y como puede ver en el resultado del comando anterior, se supone que el día es “hoy”. Para obtener el tiempo a partir de la época:

$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

La fecha de Busybox puede incluso recibir la hora (en el formato anterior) sin -D:

$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

Y el formato de salida podría incluso ser segundos desde la época.

$ date -u -d "1970.01.01-$string1" +"%s"
52436

Para ambas ocasiones, y un poco de matemáticas bash (busybox no puede hacer las matemáticas, todavía):

string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))

O formateado:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14

Así es como lo hice:

START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'

Realmente simple, tome el número de segundos al principio, luego tome el número de segundos al final e imprima la diferencia en minutos: segundos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *