Comment calculer le temps écoulé dans le script bash?

224

J'imprime l'heure de début et de fin en utilisant date +"%T", ce qui donne quelque chose comme:

10:33:56
10:36:10

Comment puis-je calculer et imprimer la différence entre ces deux?

J'aimerais obtenir quelque chose comme:

2m 14s
Misha Moroshko
la source
5
Sur une autre note, ne pourriez-vous pas utiliser la timecommande?
anishsane
Utilisez plutôt le temps Unix date +%s, puis soustrayez pour obtenir la différence en secondes.
roblogic

Réponses:

476

Bash a une SECONDSvariable intégrée pratique qui suit le nombre de secondes qui se sont écoulées depuis le démarrage du shell. Cette variable conserve ses propriétés lorsqu'elle est affectée à, et la valeur renvoyée après l'affectation est le nombre de secondes écoulées depuis l'affectation plus la valeur affectée.

Ainsi, vous pouvez simplement mettre SECONDSà 0 avant de démarrer l'événement chronométré, simplement lire SECONDSaprès l'événement et effectuer l'arithmétique temporelle avant l'affichage.

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

Comme cette solution ne dépend pas date +%s(qui est une extension GNU), elle est portable sur tous les systèmes pris en charge par Bash.

Daniel Kamil Kozar
la source
59
+1. Soit dit en passant, vous pouvez tromper dateen effectuant l'arithmétique du temps pour vous, en écrivant date -u -d @"$diff" +'%-Mm %-Ss'. (Cela s'interprète $diffcomme des secondes depuis l'époque et calcule les minutes et les secondes en UTC.) Ce n'est probablement pas plus élégant, cependant, mais mieux caché. :-P
ruakh
13
Agréable et simple. Si quelqu'un a besoin d'heures:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
chus
6
Il doit être lu $(date -u +%s)pour éviter une condition de concurrence aux dates où l'heure d'été est commutée. Et méfiez-vous des mauvaises secondes de saut;)
Tino
3
@ daniel-kamil-kozar Belle trouvaille. Mais c'est un peu fragile. Il n'y a qu'une seule variable de ce type, elle ne peut donc pas être utilisée de manière récursive pour mesurer les performances, et pire encore, une fois unset SECONDSqu'elle a disparu:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino
6
@ParthianShot: Je suis désolé que vous le pensiez. Cependant, je crois que cette réponse est ce dont la plupart des gens ont besoin lorsqu'ils recherchent des réponses à une question comme celle-ci: mesurer le temps écoulé entre les événements, pas calculer le temps.
Daniel Kamil Kozar
99

Secondes

Pour mesurer le temps écoulé (en secondes), nous avons besoin de:

  • un entier qui représente le nombre de secondes écoulées et
  • un moyen de convertir un tel entier dans un format utilisable.

Une valeur entière de secondes écoulées:

  • Il existe deux méthodes internes bash pour trouver une valeur entière pour le nombre de secondes écoulées:

    1. Variable bash SECONDS (si SECONDS n'est pas définie, elle perd sa propriété spéciale).

      • Définition de la valeur de SECONDS sur 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Mémorisation de la valeur de la variable SECONDSau départ:

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
    2. Option 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 ))

Convertir un tel entier dans un format utilisable

Le bash internal printfpeut le faire directement:

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

De même

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

mais cela échouera pour des durées de plus de 24 heures, car nous imprimons en fait une heure d'horloge murale, pas vraiment une durée:

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

Pour les amoureux du détail, sur bash-hackers.org :

%(FORMAT)Trenvoie la chaîne date-heure résultant de l'utilisation de FORMAT comme chaîne de format pour strftime(3). L'argument associé est le nombre de secondes depuis Epoch , ou -1 (heure actuelle) ou -2 (heure de démarrage du shell). Si aucun argument correspondant n'est fourni, l'heure actuelle est utilisée par défaut.

Vous pouvez donc simplement appeler textifyDuration $elpasedsecondsoù se textifyDurationtrouve une autre implémentation de l'impression de durée:

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)"
}

Date GNU.

Pour obtenir du temps formaté, nous devons utiliser un outil externe (date GNU) de plusieurs manières pour obtenir une durée pouvant atteindre près d'un an, y compris les nanosecondes.

Math à l'intérieur de la date.

Il n'y a pas besoin d'arithmétique externe, faites tout en une seule étape à l'intérieur date:

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

Oui, il y a un 0zéro dans la chaîne de commande. C'est nécessaire.

Cela suppose que vous pouvez modifier la date +"%T"commande en une date +"%s"commande afin que les valeurs soient stockées (imprimées) en quelques secondes.

Notez que la commande est limitée à:

  • Valeurs positives $StartDateet $FinalDatesecondes.
  • La valeur de $FinalDateest plus grande (plus tard dans le temps) que $StartDate.
  • Différence horaire inférieure à 24 heures.
  • Vous acceptez un format de sortie avec heures, minutes et secondes. Très facile à changer.
  • Il est acceptable d'utiliser -u UTC fois. Pour éviter "DST" et les corrections d'heure locale.

Si vous devez utiliser la 10:33:56chaîne, eh bien, convertissez-la simplement en secondes,
aussi, le mot secondes pourrait être abrégé en 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"

Notez que la conversion de temps en secondes (comme présenté ci-dessus) est relative au début de "ce" jour (Aujourd'hui).


Le concept pourrait être étendu aux nanosecondes, comme ceci:

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"

S'il est nécessaire de calculer des décalages horaires plus longs (jusqu'à 364 jours), nous devons utiliser le début de (certaines) années comme référence et la valeur de format %j(le numéro du jour dans l'année):

Semblable à:

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

Malheureusement, dans ce cas, nous devons soustraire manuellement 1UN du nombre de jours. La commande date considère le premier jour de l'année comme 1. Pas si difficile ...

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[@]}"



L'utilisation d'un long nombre de secondes est valide et documentée ici:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Date de la boîte occupée

Un outil utilisé dans les petits appareils (un très petit exécutable à installer): Busybox.

Faites un lien vers la boîte occupée appelée date:

$ ln -s /bin/busybox date

Utilisez-le ensuite en l'appelant date(placez-le dans un répertoire PATH inclus).

Ou créez un alias comme:

$ alias date='busybox date'

La date Busybox a une option intéressante: -D pour recevoir le format de l'heure d'entrée. Cela ouvre de nombreux formats à utiliser comme temps. En utilisant l'option -D, nous pouvons convertir directement l'heure 10:33:56:

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

Et comme vous pouvez le voir sur la sortie de la commande ci-dessus, le jour est supposé être "aujourd'hui". Pour que l'heure commence à l'époque:

$ 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 date Busybox peut même recevoir l'heure (dans le format ci-dessus) sans -D:

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

Et le format de sortie pourrait même être de quelques secondes depuis l'époque.

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

Pour les deux fois, et un peu de maths bash (busybox ne peut pas encore faire le calcul):

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 ))

Ou formaté:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
David Tonhofer
la source
6
C'est une réponse tellement étonnante qui couvre beaucoup de bases et de belles explications. Je vous remercie!
Devy
J'ai dû rechercher la %(FORMAT)Tchaîne passée à la printfcommande intégrée dans bash. Depuis bash-hackers.org : affichez %(FORMAT)Tla chaîne date-heure résultant de l'utilisation FORMATcomme chaîne de format pour strftime (3). L'argument associé est le nombre de secondes depuis Epoch, ou -1 (heure actuelle) ou -2 (heure de démarrage du shell). Si aucun argument correspondant n'est fourni, l'heure actuelle est utilisée par défaut . C'est ésotérique. Dans ce cas, %stel qu'indiqué à strftime(3)est "le nombre de secondes depuis l'époque".
David Tonhofer
35

Voici comment je l'ai fait:

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

Vraiment simple, prenez le nombre de secondes au début, puis prenez le nombre de secondes à la fin et imprimez la différence en minutes: secondes.

Dorian
la source
6
En quoi est-ce différent de ma solution? Je ne vois vraiment pas l'avantage d'appeler awkdans ce cas, car Bash gère aussi bien l'arithmétique entière.
Daniel Kamil Kozar
1
Votre réponse est également correcte. Certaines personnes, comme moi, préfèrent travailler avec awk qu'avec des incohérences bash.
Dorian
2
Pourriez-vous nous en dire plus sur les incohérences de Bash concernant l'arithmétique entière? J'aimerais en savoir plus à ce sujet, car je n'en connaissais aucun.
Daniel Kamil Kozar du
12
Je cherchais juste ça. Je ne comprends pas la critique de cette réponse. J'aime voir plus d'une solution à un problème. Et, je suis celui qui préfère les awkcommandes à bash (si pour rien d'autre, car awk fonctionne dans d'autres shells). J'ai mieux aimé cette solution. Mais c'est mon opinion personnelle.
rpsml
4
Zéros non significatifs: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Jon Strayer
17

Une autre option consiste à utiliser à datediffpartir de 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

Vous pouvez également utiliser gawk. mawk1.3.4 a également strftimeet mktimemais les anciennes versions de mawket nawkne le font pas.

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

Ou voici une autre façon de le faire avec 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
la source
1
Je cherchais quelque chose comme votre date méthode GNU . Génie.
Haohmaru
Veuillez supprimer mon commentaire ... désolé
Bill Gale
Après l'installation dateutilsvia apt, il n'y avait aucune datediffcommande - a dû utiliser dateutils.ddiff.
Suzana
12

Je voudrais proposer une autre façon d'éviter de rappeler la datecommande. Il peut être utile dans le cas où vous avez déjà collecté des horodatages au %Tformat date:

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"

nous pouvons même gérer des millisecondes de la même manière.

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
la source
1
Existe-t-il un moyen de gérer les millisecondes, par exemple 10: 33: 56.104
Umesh Rajbhandari
La gestion en millisecondes se comporte mal si le champ en millisecondes commence par un zéro. Par exemple, ms = "033" donnera des chiffres de fin de "027" car le champ ms est interprété comme octal. changer "echo" ... "+ ms))" en ... "+ $ {ms ## + (0)}))" corrigera cela tant que "shopt -s extglob" apparaît quelque part au-dessus de cela dans le scénario. On devrait probablement aussi retirer les zéros de tête de h, m et s ...
Eric Towers
3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))travaillé pour moi depuis que je suis arrivévalue too great for base (error token is "08")
Niklas
6

Voici un peu de magie:

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

sedremplace a :par une formule à convertir en 1/60. Ensuite, le calcul du temps qui est fait parbc

parfumé
la source
5

Depuis la date (GNU coreutils) 7.4, vous pouvez maintenant utiliser -d pour faire de l'arithmétique:

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

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

Les unités que vous pouvez utiliser sont les jours, les années, les mois, les heures, les minutes et les secondes:

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

Suite à la réponse de Daniel Kamil Kozar, pour montrer les heures / minutes / secondes:

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

Le script complet serait donc:

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

Ou envelopper un peu

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

Alors ça marche.

timerstart; sleep 2; timerstop
seconds=2
user3561136
la source
3

Voici une solution utilisant uniquement les datecapacités de commandes utilisant "ago", et n'utilisant pas une deuxième variable pour stocker l'heure de fin:

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

cela donne:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120
Zoltan K.
la source
2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds
Joseph
la source
6
Il serait utile de savoir ce que fait cette réponse que les autres réponses ne font pas.
Louis
Il vous permet de sortir facilement la durée dans n'importe quel format autorisé par date.
Ryan C. Thompson
2

date peut vous donner la différence et la formater pour vous (options OS X montrées)

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

Certains traitements de chaîne peuvent supprimer ces valeurs vides

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

Cela ne fonctionnera pas si vous placez d'abord la première fois. Si vous devez gérer cela, passez $(($(date ...) - $(date ...)))à$(echo $(date ...) - $(date ...) | bc | tr -d -)

Dave
la source
1

J'avais besoin d'un script de décalage horaire à utiliser avec mencoder( --endposc'est relatif), et ma solution est d'appeler un script Python:

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

des fractions de secondes sont également prises en charge:

$ 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)

et cela peut vous dire que la différence entre 200 et 120 est de 1h 20m:

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

et peut convertir n'importe quel nombre (probablement fractionnaire) de secondes ou minutes ou heures en 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
la source
1

Avec 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
user5207022
la source
1

Généraliser la solution de @ nisetama en utilisant la date GNU (fidèle 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

rendement:

Wed Feb 7 12:31:16 CST 2018
Wed Feb 7 12:32:25 CST 2018
00:01:09
Bill Gale
la source
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
la source
1

Définissez cette fonction (par exemple dans ~ / .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
}

Vous pouvez maintenant mesurer le temps de parties de vos scripts:

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

$ ./script.sh
Total time: 0.5060

très utile pour trouver les goulots d'étranglement d'exécution.

sergio
la source
0

Je me rends compte que c'est un article plus ancien, mais je l'ai rencontré aujourd'hui en travaillant sur un script qui prendrait les dates et les heures d'un fichier journal et calculerait le delta. Le script ci-dessous est certainement exagéré, et je recommande fortement de vérifier ma logique et mes calculs.

#!/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."

Vous obtiendrez la sortie comme suit.

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.

J'ai un peu modifié le script pour le rendre autonome (c'est-à-dire simplement définir des valeurs de variable), mais peut-être que l'idée générale se présente également. Vous voudrez peut-être des erreurs supplémentaires pour vérifier les valeurs négatives.

Daniel Blackmon
la source
Gee, beaucoup de travail !!. Veuillez regarder ma réponse: stackoverflow.com/a/24996647/2350426
0

Je ne peux pas commenter la réponse de mcaleaa, donc je poste ceci ici. La variable "diff" doit être sur une petite casse. Voici un exemple.

[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 précédente a été déclarée en minuscules. C'est ce qui s'est produit lorsque des majuscules sont utilisées.

[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 ~]# 

Donc, une solution rapide serait comme ça

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

Voici mon implémentation bash (avec des bits provenant d'autres 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

Non testé, peut être buggé.

Ondra Žižka
la source