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

224

Imprimo el tiempo de inicio y finalización usando date +"%T", lo que resulta en algo como:

10:33:56
10:36:10

¿Cómo podría calcular e imprimir la diferencia entre estos dos?

Me gustaría obtener algo como:

2m 14s
Misha Moroshko
fuente
55
En otra nota, ¿no podrías usar el timecomando?
anishsane
Use el tiempo unix en su lugar, date +%sluego reste para obtener la diferencia en segundos.
roblogic

Respuestas:

476

Bash tiene una práctica SECONDSvariable integrada que rastrea la cantidad de segundos que han pasado desde que se inició el shell. Esta variable conserva sus propiedades cuando se le asigna, 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 establecer SECONDSen 0 antes de comenzar el evento cronometrado, simplemente leer SECONDSdespués del evento y hacer la aritmética del 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 date +%s(que es una extensión GNU), es portátil para todos los sistemas compatibles con Bash.

Daniel Kamil Kozar
fuente
59
+1. Por cierto, puedes engañar datepara que realices la aritmética del tiempo por ti, escribiendo date -u -d @"$diff" +'%-Mm %-Ss'. (Eso se interpreta $diffcomo segundos desde la época, y calcula los minutos y segundos en UTC). Sin embargo, eso probablemente no sea más elegante, pero mejor ofuscado. :-P
ruakh
13
Agradable y simple Si alguien necesita horas:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
Chus
66
Debe leer $(date -u +%s)para evitar una condición de carrera en las fechas en que se cambia el horario de verano. Y cuidado con los segundos de salto malvados;)
Tino
3
@ daniel-kamil-kozar Buen hallazgo. Sin embargo, esto es un poco frágil. Solo existe una de esas variables, por lo que no se puede usar de forma recursiva para medir el rendimiento, y lo que es peor, una vez unset SECONDSque desaparece:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino
66
@ParthianShot: Lamento que lo pienses así. Sin embargo, creo que esta respuesta es lo que la mayoría de la gente necesita mientras busca respuestas a una pregunta como esta: para medir cuánto tiempo pasó entre eventos, no para calcular el tiempo.
Daniel Kamil Kozar
99

Segundos

Para medir el tiempo transcurrido (en segundos) necesitamos:

  • un 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 la cantidad de segundos transcurridos:

    1. Variable Bash SECONDS (si SECONDS no está configurado, pierde su propiedad especial).

      • Establecer el valor de SEGUNDOS en 0:

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

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

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

Convierta dicho entero a un formato utilizable

El bash interno printfpuede hacer eso directamente:

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

similar

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

pero esto fallará por más de 24 horas, ya que imprimimos un tiempo 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)T\n' "$elapsedseconds"
06:12:24

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

%(FORMAT)Tgenera 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 (hora actual) o -2 (hora de inicio del shell). Si no se proporciona ningún argumento correspondiente, la hora actual se usa como predeterminada.

Por lo tanto, puede llamar a textifyDuration $elpasedsecondsdonde textifyDurationhay otra implementación de 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 tiempo formateado, debemos usar una herramienta externa (fecha GNU) de varias maneras para obtener una duración de casi un año e incluir nanosegundos.

Matemáticas dentro de la fecha.

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

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

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

Eso supone que podría 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 $StartDatey $FinalDatesegundos.
  • El valor en $FinalDatees mayor (más tarde) que $StartDate.
  • Diferencia horaria menor a 24 horas.
  • Acepta un formato de salida con Horas, Minutos y Segundos. Muy facil de cambiar.
  • Es aceptable usar -u UTC veces. Para evitar "DST" y correcciones de hora local.

Si debe usar la 10:33:56cadena, bueno, simplemente conviértala a segundos,
también, la palabra segundos podría abreviarse 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 del tiempo de segundos (como se presentó anteriormente) es relativa al comienzo del día "este" (Hoy).


El concepto podría extenderse a nanosegundos, como este:

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 (algunos) años como referencia y el valor de formato %j(el número de día en el 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, necesitamos restar 1UNO manualmente 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 luego llamando a esto date(colóquelo en un directorio incluido PATH).

O haga un alias como:

$ alias date='busybox date'

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

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

Y como puede ver en la salida del Comando anterior, se supone que el día es "hoy". Para comenzar el tiempo en é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 ser incluso segundos desde la época.

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

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

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
David Tonhofer
fuente
66
Esta es una respuesta tan sorprendente que abarca muchas bases y con buenas explicaciones. ¡Gracias!
Devy
Tuve que buscar la %(FORMAT)Tcadena pasada al printfcomando integrado en bash. Desde bash-hackers.org : genera %(FORMAT)Tla cadena de fecha y hora resultante del uso FORMATcomo cadena de formato para strftime (3). El argumento asociado es el número de segundos desde Epoch, o -1 (hora actual) o -2 (hora de inicio del shell). Si no se suministra ningún argumento correspondiente, la hora actual se usa como predeterminada . Eso es esotérico. En este caso, %scomo se da a strftime(3)es "el número de segundos desde la época".
David Tonhofer
35

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 la cantidad de segundos al comienzo, luego tome la cantidad de segundos al final e imprima la diferencia en minutos: segundos.

dorio
fuente
66
¿Cómo es eso diferente de mi solución? Realmente no puedo ver el beneficio de llamar awken este caso, ya que Bash maneja la aritmética de enteros igualmente bien.
Daniel Kamil Kozar
1
Tu respuesta es correcta también. Algunas personas, como yo, prefieren trabajar con awk que con inconsistencias bash.
Dorian
2
¿Podría dar más detalles sobre las inconsistencias en Bash con respecto a la aritmética de enteros? Me gustaría saber más sobre esto, ya que no sabía nada.
Daniel Kamil Kozar
12
Solo estaba buscando esto. No entiendo las críticas a esta respuesta. Me gusta ver más de una solución a un problema. Y, soy uno que prefiere los awkcomandos a bash (aunque sea por nada más, porque awk funciona en otros shells). Me gustó más esta solución. Pero esa es mi opinión personal.
rpsml
44
Ceros a la izquierda: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Jon Strayer
17

Otra opción es usar datediffdesde dateutils( http://www.fresse.org/dateutils/#datediff ):

$ datediff 10:33:56 10:36:10
134s
$ datediff 10:33:56 10:36:10 -f%H:%M:%S
0:2:14
$ datediff 10:33:56 10:36:10 -f%0H:%0M:%0S
00:02:14

También podrías usar gawk. mawk1.3.4 también tiene strftimey mktimepero versiones anteriores de mawky nawkno.

$ TZ=UTC0 awk 'BEGIN{print strftime("%T",mktime("1970 1 1 10 36 10")-mktime("1970 1 1 10 33 56"))}'
00:02:14

O aquí hay otra forma de hacerlo con GNU date:

$ date -ud@$(($(date -ud'1970-01-01 10:36:10' +%s)-$(date -ud'1970-01-01 10:33:56' +%s))) +%T
00:02:14
nisetama
fuente
1
Estaba buscando algo como tu date método GNU . Genio.
Haohmaru
Por favor, elimine mi comentario ... lo siento
Bill Gale
Después de la instalación a dateutilstravés de apt, no hubo ningún datediffcomando, tuve que usar dateutils.ddiff.
Suzana
12

Me gustaría proponer otra forma que evite recordar el datecomando. Puede ser útil en caso de que ya haya reunido marcas de tiempo en %Tformato de fecha:

ts_get_sec()
{
  read -r h m s <<< $(echo $1 | tr ':' ' ' )
  echo $(((h*60*60)+(m*60)+s))
}

start_ts=10:33:56
stop_ts=10:36:10

START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))

echo "$((DIFF/60))m $((DIFF%60))s"

incluso podemos manejar milisegundos de la misma manera.

ts_get_msec()
{
  read -r h m s ms <<< $(echo $1 | tr '.:' ' ' )
  echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}

start_ts=10:33:56.104
stop_ts=10:36:10.102

START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))

min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))

echo "${min}:${sec}.$ms"
Zskdan
fuente
1
¿Hay alguna manera de manejar miliseconos, por ejemplo 10: 33: 56.104
Umesh Rajbhandari
El manejo de milisegundos se comporta mal si el campo de milisegundos comienza con un cero. Por ejemplo, ms = "033" dará los dígitos finales de "027" porque el campo ms se interpreta como octal. cambiar "echo" ... "+ ms))" a ... "+ $ {ms ## + (0)}))" solucionará esto siempre y cuando "shopt -s extglob" aparezca en algún lugar sobre esto en el guión. Probablemente uno también debería quitar los ceros a la izquierda de h, m y s también ...
Eric Towers
3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))funcionó para mí desde que lleguévalue too great for base (error token is "08")
Niklas
6

Aquí hay algo de magia:

time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$(  echo "$time2 - $time1"  | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours

sedreemplaza a :con una fórmula para convertir a 1/60. Luego, el cálculo del tiempo que realizabc

fragante
fuente
5

A partir de la fecha (GNU coreutils) 7.4 ahora puede usar -d para hacer aritmética:

$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014

$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014

Las unidades que puede usar son días, años, meses, horas, minutos y segundos:

$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014
harapos
fuente
3

Siguiendo la respuesta de Daniel Kamil Kozar, para mostrar horas / minutos / segundos:

echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

Entonces el guión completo sería:

date1=$(date +"%s")
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
mcaleaa
fuente
3

O envuélvelo un poco

alias timerstart='starttime=$(date +"%s")'
alias timerstop='echo seconds=$(($(date +"%s")-$starttime))'

Entonces esto funciona.

timerstart; sleep 2; timerstop
seconds=2
usuario3561136
fuente
3

Aquí hay una solución que usa solo las datecapacidades de comandos usando "ago", y no usando una segunda variable para almacenar el tiempo de finalización:

#!/bin/bash

# save the current time
start_time=$( date +%s.%N )

# tested program
sleep 1

# the current time after the program has finished
# minus the time when we started, in seconds.nanoseconds
elapsed_time=$( date +%s.%N --date="$start_time seconds ago" )

echo elapsed_time: $elapsed_time

esto da:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120
Zoltan K.
fuente
2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds
Joseph
fuente
66
Sería útil saber qué hace esta respuesta que las otras respuestas no hacen.
Louis
Le permite generar fácilmente la duración en cualquier formato permitido por date.
Ryan C. Thompson
2

date puede darle la diferencia y formatearla por usted (se muestran las opciones de OS X)

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) +%T
# 00:02:14

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss'
# 0h 2m 14s

Algunos procesamientos de cadenas pueden eliminar esos valores vacíos

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss' | sed "s/[[:<:]]0[hms] *//g"
# 2m 14s

Esto no funcionará si coloca la primera vez primero. Si necesita manejar eso, cambie $(($(date ...) - $(date ...)))a$(echo $(date ...) - $(date ...) | bc | tr -d -)

Dave
fuente
1

Necesitaba un script de diferencia horaria para usar con mencoder( --endposes relativo), y mi solución es llamar a un script de Python:

$ ./timediff.py 1:10:15 2:12:44
1:02:29

fracciones de segundos también son compatibles:

$ echo "diff is `./timediff.py 10:51.6 12:44` (in hh:mm:ss format)"
diff is 0:01:52.4 (in hh:mm:ss format)

y te puede decir que la diferencia entre 200 y 120 es 1h 20m:

$ ./timediff.py 120:0 200:0
1:20:0

y puede convertir cualquier número (probablemente fraccionario) de segundos o minutos u horas a hh: mm: ss

$ ./timediff.py 0 3600
1:00:0
$ ./timediff.py 0 3.25:0:0
3:15:0

timediff.py:

#!/usr/bin/python

import sys

def x60(h,m):
    return 60*float(h)+float(m)

def seconds(time):
    try:
       h,m,s = time.split(':')
       return x60(x60(h,m),s)
    except ValueError:
       try:
          m,s = time.split(':')
          return x60(m,s)
       except ValueError:
          return float(time)

def difftime(start, end):
    d = seconds(end) - seconds(start)
    print '%d:%02d:%s' % (d/3600,d/60%60,('%02f' % (d%60)).rstrip('0').rstrip('.'))

if __name__ == "__main__":
   difftime(sys.argv[1],sys.argv[2])
18446744073709551615
fuente
1

Con GNU units:

$ units
2411 units, 71 prefixes, 33 nonlinear units
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: s
    * 134
    / 0.0074626866
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: min
    * 2.2333333
    / 0.44776119
usuario5207022
fuente
1

Generalizando la solución de @ nisetama usando la fecha GNU (confiable Ubuntu 14.04 LTS):

start=`date`
# <processing code>
stop=`date`
duration=`date -ud@$(($(date -ud"$stop" +%s)-$(date -ud"$start" +%s))) +%T`

echo $start
echo $stop
echo $duration

flexible:

Wed Feb 7 12:31:16 CST 2018
Wed Feb 7 12:32:25 CST 2018
00:01:09
Bill Gale
fuente
1
#!/bin/bash

START_TIME=$(date +%s)

sleep 4

echo "Total time elapsed: $(date -ud "@$(($(date +%s) - $START_TIME))" +%T) (HH:MM:SS)"
$ ./total_time_elapsed.sh 
Total time elapsed: 00:00:04 (HH:MM:SS)
David
fuente
1

Defina esta función (digamos en ~ / .bashrc):

time::clock() {
    [ -z "$ts" ]&&{ ts=`date +%s%N`;return;}||te=`date +%s%N`
    printf "%6.4f" $(echo $((te-ts))/1000000000 | bc -l)
    unset ts te
}

Ahora puede medir el tiempo de partes de sus scripts:

$ cat script.sh
# ... code ...
time::clock
sleep 0.5
echo "Total time: ${time::clock}"
# ... more code ...

$ ./script.sh
Total time: 0.5060

Muy útil para encontrar cuellos de botella en la ejecución.

sergio
fuente
0

Me doy cuenta de que esta es una publicación anterior, pero la encontré hoy mientras trabajaba en un script que tomaría fechas y horas de un archivo de registro y calcularía el delta. La secuencia de comandos a continuación es ciertamente exagerada, y recomiendo comprobar mi lógica y matemáticas.

#!/bin/bash

dTime=""
tmp=""

#firstEntry="$(head -n 1 "$LOG" | sed 's/.*] \([0-9: -]\+\).*/\1/')"
firstEntry="2013-01-16 01:56:37"
#lastEntry="$(tac "$LOG" | head -n 1 | sed 's/.*] \([0-9: -]\+\).*/\1/')"
lastEntry="2014-09-17 18:24:02"

# I like to make the variables easier to parse
firstEntry="${firstEntry//-/ }"
lastEntry="${lastEntry//-/ }"
firstEntry="${firstEntry//:/ }"
lastEntry="${lastEntry//:/ }"

# remove the following lines in production
echo "$lastEntry"
echo "$firstEntry"

# compute days in last entry
for i in `seq 1 $(echo $lastEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime+31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime+30))
    ;;
   2 )
    dTime=$(($dTime+28))
    ;;
  esac
} done

# do leap year calculations for all years between first and last entry
for i in `seq $(echo $firstEntry|awk '{print $1}') $(echo $lastEntry|awk '{print $1}')`; do {
  if [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -eq 0 ] && [ $(($i%400)) -eq 0 ]; then {
    if [ "$i" = "$(echo $firstEntry|awk '{print $1}')" ] && [ $(echo $firstEntry|awk '{print $2}') -lt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $firstEntry|awk '{print $2}') -eq 2 ] && [ $(echo $firstEntry|awk '{print $3}') -lt 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } elif [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -ne 0 ]; then {
    if [ "$i" = "$(echo $lastEntry|awk '{print $1}')" ] && [ $(echo $lastEntry|awk '{print $2}') -gt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $lastEntry|awk '{print $2}') -eq 2 ] && [ $(echo $lastEntry|awk '{print $3}') -ne 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } fi
} done

# substract days in first entry
for i in `seq 1 $(echo $firstEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime-31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime-30))
    ;;
   2 )
    dTime=$(($dTime-28))
    ;;
  esac
} done

dTime=$(($dTime+$(echo $lastEntry|awk '{print $3}')-$(echo $firstEntry|awk '{print $3}')))

# The above gives number of days for sample. Now we need hours, minutes, and seconds
# As a bit of hackery I just put the stuff in the best order for use in a for loop
dTime="$(($(echo $lastEntry|awk '{print $6}')-$(echo $firstEntry|awk '{print $6}'))) $(($(echo $lastEntry|awk '{print $5}')-$(echo $firstEntry|awk '{print $5}'))) $(($(echo $lastEntry|awk '{print $4}')-$(echo $firstEntry|awk '{print $4}'))) $dTime"
tmp=1
for i in $dTime; do {
  if [ $i -lt 0 ]; then {
    case "$tmp" in
     1 )
      tmp="$(($(echo $dTime|awk '{print $1}')+60)) $(($(echo $dTime|awk '{print $2}')-1))"
      dTime="$tmp $(echo $dTime|awk '{print $3" "$4}')"
      tmp=1
      ;;
     2 )
      tmp="$(($(echo $dTime|awk '{print $2}')+60)) $(($(echo $dTime|awk '{print $3}')-1))"
      dTime="$(echo $dTime|awk '{print $1}') $tmp $(echo $dTime|awk '{print $4}')"
      tmp=2
      ;;
     3 )
      tmp="$(($(echo $dTime|awk '{print $3}')+24)) $(($(echo $dTime|awk '{print $4}')-1))"
      dTime="$(echo $dTime|awk '{print $1" "$2}') $tmp"
      tmp=3
      ;;
    esac
  } fi
  tmp=$(($tmp+1))
} done

echo "The sample time is $(echo $dTime|awk '{print $4}') days, $(echo $dTime|awk '{print $3}') hours, $(echo $dTime|awk '{print $2}') minutes, and $(echo $dTime|awk '{print $1}') seconds."

Obtendrá la salida de la siguiente manera.

2012 10 16 01 56 37
2014 09 17 18 24 02
The sample time is 700 days, 16 hours, 27 minutes, and 25 seconds.

Modifiqué un poco el script para que sea independiente (es decir, simplemente establezca valores variables), pero tal vez la idea general también aparezca. Es posible que desee alguna comprobación de error adicional para valores negativos.

Daniel Blackmon
fuente
¡Vaya, mucho trabajo! Mire mi respuesta: stackoverflow.com/a/24996647/2350426
0

No puedo comentar sobre la respuesta de mcaleaa, por lo tanto, publico esto aquí. La variable "diff" debe estar en minúsculas. Aquí hay un ejemplo.

[root@test ~]# date1=$(date +"%s"); date
Wed Feb 21 23:00:20 MYT 2018
[root@test ~]# 
[root@test ~]# date2=$(date +"%s"); date
Wed Feb 21 23:00:33 MYT 2018
[root@test ~]# 
[root@test ~]# diff=$(($date2-$date1))
[root@test ~]# 

La variable anterior se declaró en minúscula. Esto es lo que sucedió cuando se usa mayúscula.

[root@test ~]# echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
-bash: / 3600 : syntax error: operand expected (error token is "/ 3600 ")
[root@test ~]# 

Entonces, una solución rápida sería así

[root@test ~]# echo "Duration: $(($diff / 3600 )) hours $((($diff % 3600) / 60)) minutes $(($diff % 60)) seconds"
Duration: 0 hours 0 minutes 13 seconds
[root@test ~]# 
Sabrina
fuente
-1

Aquí está mi implementación de bash (con bits tomados de otro SO ;-)

function countTimeDiff() {
    timeA=$1 # 09:59:35
    timeB=$2 # 17:32:55

    # feeding variables by using read and splitting with IFS
    IFS=: read ah am as <<< "$timeA"
    IFS=: read bh bm bs <<< "$timeB"

    # Convert hours to minutes.
    # The 10# is there to avoid errors with leading zeros
    # by telling bash that we use base 10
    secondsA=$((10#$ah*60*60 + 10#$am*60 + 10#$as))
    secondsB=$((10#$bh*60*60 + 10#$bm*60 + 10#$bs))
    DIFF_SEC=$((secondsB - secondsA))
    echo "The difference is $DIFF_SEC seconds.";

    SEC=$(($DIFF_SEC%60))
    MIN=$((($DIFF_SEC-$SEC)%3600/60))
    HRS=$((($DIFF_SEC-$MIN*60)/3600))
    TIME_DIFF="$HRS:$MIN:$SEC";
    echo $TIME_DIFF;
}

$ countTimeDiff 2:15:55 2:55:16
The difference is 2361 seconds.
0:39:21

No probado, puede tener errores.

Ondra Žižka
fuente