Wie berechnet man die im Bash-Skript verstrichene Zeit?


225

Ich drucke die Start- und Endzeit mit date +"%T", was zu etwas führt wie:

10:33:56
10:36:10

Wie könnte ich die Differenz zwischen diesen beiden berechnen und ausdrucken?

Ich möchte etwas bekommen wie:

2m 14s

5
Könnten Sie den timeBefehl nicht verwenden ?
Anishsane

Verwenden Sie stattdessen die Unix-Zeit date +%sund subtrahieren Sie dann, um die Differenz in Sekunden zu erhalten.
Roblogic

Antworten:


475

Bash verfügt über eine praktische SECONDSintegrierte Variable, die die Anzahl der Sekunden verfolgt, die seit dem Start der Shell vergangen sind. Diese Variable behält ihre Eigenschaften bei, wenn sie zugewiesen wird, und der nach der Zuweisung zurückgegebene Wert gibt die Anzahl der Sekunden seit der Zuweisung plus den zugewiesenen Wert an.

Sie können also einfach SECONDSvor dem Starten des zeitgesteuerten Ereignisses auf 0 setzen , einfach SECONDSnach dem Ereignis lesen und die Zeitarithmetik vor der Anzeige durchführen.

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

Da diese Lösung nicht von date +%s(einer GNU-Erweiterung) abhängt , kann sie auf alle von Bash unterstützten Systeme portiert werden.


59
+1. Übrigens können dateSie durch Schreiben dazu verleiten , die Zeitarithmetik für Sie durchzuführen date -u -d @"$diff" +'%-Mm %-Ss'. (Das wird $diffals Sekunden seit der Epoche interpretiert und berechnet die Minuten und Sekunden in UTC.) Das ist wahrscheinlich nicht eleganter, nur besser verschleiert. :-P
Ruakh

13
Schön und einfach. Wenn jemand Stunden braucht:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
Chus

6
Es sollte gelesen werden $(date -u +%s), um eine Rennbedingung an Daten zu verhindern, an denen die Sommerzeit umgeschaltet wird. Und hüte dich vor den bösen Schaltsekunden;)
Tino

3
@ daniel-kamil-kozar Schöner Fund. Dies ist jedoch ein bisschen zerbrechlich. Es gibt nur eine solche Variable, daher kann sie nicht rekursiv verwendet werden, um die Leistung zu messen, und noch schlimmer, wenn unset SECONDSsie weg ist:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino

6
@ParthianShot: Es tut mir leid, dass Sie so denken. Ich glaube jedoch, dass diese Antwort das ist, was die meisten Menschen brauchen, wenn sie nach Antworten auf eine Frage wie diese suchen: um zu messen, wie viel Zeit zwischen Ereignissen vergangen ist, und nicht um die Zeit zu berechnen.
Daniel Kamil Kozar

99

Sekunden

Um die verstrichene Zeit (in Sekunden) zu messen, benötigen wir:

  • eine Ganzzahl, die die Anzahl der verstrichenen Sekunden und darstellt
  • eine Möglichkeit, eine solche Ganzzahl in ein verwendbares Format zu konvertieren.

Ein ganzzahliger Wert der verstrichenen Sekunden:

  • Es gibt zwei interne Bash-Methoden, um einen ganzzahligen Wert für die Anzahl der verstrichenen Sekunden zu ermitteln:

    1. Bash-Variable SECONDS (wenn SECONDS nicht gesetzt ist, verliert es seine spezielle Eigenschaft).

      • Setzen Sie den Wert von SECONDS auf 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Speichern des Werts der Variablen SECONDSzu Beginn:

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

Konvertieren Sie eine solche Ganzzahl in ein verwendbares Format

Der Bash Internal printfkann das direkt tun:

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

ähnlich

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

aber dies wird für eine Dauer von mehr als 24 Stunden ausfallen, als wir tatsächlich eine Wanduhr Zeit drucken, nicht wirklich eine Dauer:

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

Für die Liebhaber von Details von bash-hackers.org :

%(FORMAT)Tgibt die Datums- / Uhrzeitzeichenfolge aus, die sich aus der Verwendung von FORMAT als Formatzeichenfolge für ergibt strftime(3). Das zugehörige Argument ist die Anzahl der Sekunden seit Epoche oder -1 (aktuelle Zeit) oder -2 (Shell-Startzeit). Wenn kein entsprechendes Argument angegeben wird, wird standardmäßig die aktuelle Uhrzeit verwendet.

Vielleicht möchten Sie einfach anrufen, textifyDuration $elpasedsecondswo textifyDurationsich noch eine weitere Implementierung des Dauerdrucks befindet:

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

GNU-Datum.

Um die formatierte Zeit zu erhalten, sollten wir ein externes Tool (GNU-Datum) auf verschiedene Arten verwenden, um eine Länge von fast einem Jahr zu erreichen, einschließlich Nanosekunden.

Mathe innerhalb des Datums.

Es ist keine externe Arithmetik erforderlich. Führen Sie alles in einem Schritt aus date:

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

Ja, 0die Befehlszeichenfolge enthält eine Null. Es wird gebraucht.

Dies setzt voraus, dass Sie den date +"%T"Befehl in einen date +"%s"Befehl ändern können , damit die Werte in Sekunden gespeichert (gedruckt) werden.

Beachten Sie, dass der Befehl auf Folgendes beschränkt ist:

  • Positive Werte von $StartDateund $FinalDateSekunden.
  • Der Wert in $FinalDateist größer (später) als $StartDate.
  • Zeitunterschied kleiner als 24 Stunden.
  • Sie akzeptieren ein Ausgabeformat mit Stunden, Minuten und Sekunden. Sehr einfach zu ändern.
  • Es ist akzeptabel, -u UTC-Zeiten zu verwenden. Um "DST" und Ortszeitkorrekturen zu vermeiden.

Wenn Sie müssen die Verwendung 10:33:56Zeichenfolge, na ja, konvertiert es nur Sekunden,
auch könnten das Wort Sekunden als sec abgekürzt werden:

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"

Beachten Sie, dass die Sekundenzeitumrechnung (wie oben dargestellt) relativ zum Beginn "dieses" Tages (heute) ist.


Das Konzept könnte folgendermaßen auf Nanosekunden erweitert werden:

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"

Wenn längere Zeitdifferenzen (bis zu 364 Tage) berechnet werden müssen, müssen wir den Jahresbeginn (einige) als Referenz und den Formatwert %j(die Tageszahl im Jahr) verwenden:

Ähnlich zu:

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

Leider müssen wir in diesem Fall 1EINS manuell von der Anzahl der Tage abziehen . Der Datumsbefehl zeigt den ersten Tag des Jahres als 1. Nicht so schwierig ...

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



Die Verwendung einer langen Anzahl von Sekunden ist hier gültig und dokumentiert:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Busybox-Datum

Ein Tool, das in kleineren Geräten verwendet wird (eine sehr kleine ausführbare Datei, die installiert werden muss): Busybox.

Stellen Sie entweder einen Link zur Busybox mit dem Namen Datum her:

$ ln -s /bin/busybox date

Verwenden Sie es dann, indem Sie dies aufrufen date(platzieren Sie es in einem PATH-enthaltenen Verzeichnis).

Oder machen Sie einen Alias ​​wie:

$ alias date='busybox date'

Das Busybox-Datum hat eine nette Option: -D, um das Format der Eingabezeit zu erhalten. Das eröffnet viele Formate, die als Zeit verwendet werden können. Mit der Option -D können wir die Zeit 10:33:56 direkt konvertieren:

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

Und wie Sie aus der Ausgabe des obigen Befehls sehen können, wird angenommen, dass der Tag "heute" ist. So starten Sie die Zeit in die Epoche:

$ 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

Das Busybox-Datum kann sogar die Uhrzeit (im obigen Format) ohne -D empfangen:

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

Und das Ausgabeformat könnte sogar Sekunden seit der Epoche betragen.

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

Für beide Male und ein wenig Bash-Mathe (Busybox kann die Mathe noch nicht machen):

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

Oder formatiert:

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

6
Dies ist eine so erstaunliche Antwort, die viele Grundlagen und feine Erklärungen abdeckt. Danke dir!
Devy

Ich musste die %(FORMAT)TZeichenfolge nachschlagen, die an den printfin bash integrierten Befehl übergeben wurde. Von bash-hackers.org : Geben Sie %(FORMAT)Tdie Datums- / FORMATUhrzeitzeichenfolge aus, die sich aus der Verwendung als Formatzeichenfolge für strftime (3) ergibt. Das zugehörige Argument ist die Anzahl der Sekunden seit Epoche oder -1 (aktuelle Zeit) oder -2 (Shell-Startzeit). Wenn kein entsprechendes Argument angegeben wird, wird standardmäßig die aktuelle Zeit verwendet . Das ist esoterisch. In diesem Fall ist %swie angegeben strftime(3)"die Anzahl der Sekunden seit der Epoche".
David Tonhofer

35

So habe ich es gemacht:

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

Ganz einfach, nehmen Sie die Anzahl der Sekunden am Anfang, dann die Anzahl der Sekunden am Ende und drucken Sie die Differenz in Minuten: Sekunden.


6
Wie unterscheidet sich das von meiner Lösung? Ich kann den Vorteil eines Aufrufs awkin diesem Fall wirklich nicht erkennen , da Bash die Ganzzahlarithmetik gleich gut verarbeitet.
Daniel Kamil Kozar

1
Ihre Antwort ist auch richtig. Einige Leute, wie ich, arbeiten lieber mit awk als mit Bash-Inkonsistenzen.
Dorian

2
Könnten Sie etwas mehr über die Inkonsistenzen in Bash in Bezug auf die Ganzzahlarithmetik erläutern? Ich würde gerne mehr darüber wissen, da mir nichts bekannt war.
Daniel Kamil Kozar

12
Ich habe nur danach gesucht. Ich verstehe die Kritik an dieser Antwort nicht. Ich möchte mehr als eine Lösung für ein Problem sehen. Und ich bin einer, der awkBefehle dem Bash vorzieht (wenn auch aus keinem anderen Grund, weil awk in anderen Shells funktioniert). Diese Lösung hat mir besser gefallen. Aber das ist meine persönliche Meinung.
rpsml

4
Führende Nullen: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Jon Strayer

17

Eine andere Option ist die Verwendung datediffvon 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

Sie könnten auch verwenden gawk. mawk1.3.4 hat auch strftimeund mktimeaber ältere Versionen von mawkund nawknicht.

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

Oder hier ist eine andere Möglichkeit, dies mit GNU zu tun 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

1
Ich habe nach so etwas wie Ihrer GNU- date Methode gesucht . Genius.
Haohmaru

Bitte löschen Sie meinen Kommentar ... Entschuldigung
Bill Gale

Nach der Installation dateutilsüber apt gab es keinen datediffBefehl - musste verwendet werden dateutils.ddiff.
Suzana

12

Ich möchte einen anderen Weg vorschlagen, um das Abrufen von dateBefehlen zu vermeiden . Dies kann hilfreich sein, wenn Sie bereits Zeitstempel im %TDatumsformat gesammelt haben :

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"

Wir können sogar Millisekunden auf die gleiche Weise behandeln.

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"

1
Gibt es eine Möglichkeit, mit Millisecons
umzugehen

Die Millisekundenbehandlung verhält sich schlecht, wenn das Millisekundenfeld mit einer Null beginnt. Zum Beispiel gibt ms = "033" nachfolgende Ziffern von "027" an, weil das ms-Feld als oktal interpretiert wird. Wenn Sie "echo" ... "+ ms))" in ... "+ $ {ms ## + (0)})" ändern, wird dies behoben, solange "shopt -s extglob" irgendwo darüber im angezeigt wird Skript. Man sollte wahrscheinlich auch führende Nullen von h, m und s entfernen ...
Eric Towers

3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))arbeitete für mich seit ich bekamvalue too great for base (error token is "08")
Niklas

6

Hier ist etwas 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

sedersetzt a :durch eine Formel, die in 1/60 konvertiert werden soll. Dann die Zeitberechnung, die von gemacht wirdbc


5

Ab dem Datum (GNU coreutils) 7.4 können Sie jetzt -d verwenden, um zu rechnen:

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

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

Die Einheiten, die Sie verwenden können, sind Tage, Jahre, Monate, Stunden, Minuten und Sekunden:

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

3

In Anlehnung an die Antwort von Daniel Kamil Kozar, um Stunden / Minuten / Sekunden anzuzeigen:

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

Das vollständige Skript wäre also:

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

3

Oder wickeln Sie es ein bisschen ein

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

Dann funktioniert das.

timerstart; sleep 2; timerstop
seconds=2

3

Hier ist eine Lösung, die nur die dateBefehlsfunktionen verwendet, die "vor" verwenden, und keine zweite Variable zum Speichern der Endzeit verwendet:

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

das gibt:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120

2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds

6
Es wäre hilfreich zu wissen, was diese Antwort bewirkt, was die anderen Antworten nicht tun.
Louis

Sie können die Dauer einfach in einem beliebigen Format ausgeben date.
Ryan C. Thompson

2

date kann Ihnen den Unterschied geben und ihn für Sie formatieren (OS X-Optionen werden angezeigt)

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

Einige Zeichenfolgenverarbeitungen können diese leeren Werte entfernen

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

Dies funktioniert nicht, wenn Sie die frühere Zeit zuerst platzieren. Wenn Sie damit umgehen müssen, wechseln Sie $(($(date ...) - $(date ...)))zu$(echo $(date ...) - $(date ...) | bc | tr -d -)


1

Ich brauchte ein Zeitunterschiedsskript zur Verwendung mit mencoder(es --endposist relativ), und meine Lösung besteht darin, ein Python-Skript aufzurufen:

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

Sekundenbruchteile werden ebenfalls unterstützt:

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

und es kann Ihnen sagen, dass der Unterschied zwischen 200 und 120 1h 20m beträgt:

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

und kann eine beliebige (wahrscheinlich gebrochene) Anzahl von Sekunden, Minuten oder Stunden in hh: mm: ss umwandeln

$ ./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])

1

Mit 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

1

Verallgemeinern der @ nisetama-Lösung unter Verwendung des GNU-Datums (vertrauenswürdiges 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

Nachgeben:

Wed Feb 7 12:31:16 CST 2018
Wed Feb 7 12:32:25 CST 2018
00:01:09

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)

1

Definieren Sie diese Funktion (z. B. in ~ / .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
}

Jetzt können Sie die Zeit von Teilen Ihrer Skripte messen:

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

$ ./script.sh
Total time: 0.5060

Sehr nützlich, um Ausführungsengpässe zu finden.


0

Mir ist klar, dass dies ein älterer Beitrag ist, aber ich bin heute darauf gestoßen, als ich an einem Skript gearbeitet habe, das Datum und Uhrzeit aus einer Protokolldatei entnimmt und das Delta berechnet. Das folgende Skript ist sicherlich übertrieben, und ich empfehle dringend, meine Logik und Mathematik zu überprüfen.

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

Sie erhalten folgende Ausgabe.

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.

Ich habe das Skript ein wenig modifiziert, um es eigenständig zu machen (dh nur variable Werte festlegen), aber vielleicht kommt auch die allgemeine Idee rüber. Möglicherweise möchten Sie zusätzliche Fehler bei der Überprüfung auf negative Werte.


Gee, viel Arbeit !!. Bitte schauen Sie sich meine Antwort an: stackoverflow.com/a/24996647/2350426

0

Ich kann die Antwort von mcaleaa nicht kommentieren, daher poste ich dies hier. Die Variable "diff" sollte sich in einem kleinen Fall befinden. Hier ist ein Beispiel.

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

Vorherige Variable wurde in Kleinbuchstaben deklariert. Dies ist passiert, wenn Großbuchstaben verwendet werden.

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

Eine schnelle Lösung wäre also so

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

-1

Hier ist meine Bash-Implementierung (mit Bits aus anderen 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

Nicht getestet, kann fehlerhaft sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.