Bagaimana cara menghitung waktu yang telah berlalu dalam skrip bash?

224

Saya mencetak waktu mulai dan berakhir menggunakan date +"%T", yang menghasilkan sesuatu seperti:

10:33:56
10:36:10

Bagaimana saya bisa menghitung dan mencetak perbedaan antara keduanya?

Saya ingin mendapatkan sesuatu seperti:

2m 14s
Misha Moroshko
sumber
5
Pada catatan lain, tidak bisakah Anda menggunakan timeperintah?
anishsane
Gunakan waktu unix date +%s,, lalu kurangi untuk mendapatkan perbedaan dalam hitungan detik.
roblogic

Jawaban:

476

Bash memiliki SECONDSvariabel bawaan berguna yang melacak jumlah detik yang telah berlalu sejak shell dimulai. Variabel ini mempertahankan sifat-sifatnya ketika ditugaskan ke, dan nilai yang dikembalikan setelah penugasan adalah jumlah detik sejak penugasan ditambah nilai yang diberikan.

Dengan demikian, Anda bisa mengatur SECONDSke 0 sebelum memulai acara waktunya, cukup membaca SECONDSsetelah acara, dan lakukan aritmatika waktu sebelum menampilkan.

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

Karena solusi ini tidak bergantung pada date +%s(yang merupakan ekstensi GNU), ini portabel untuk semua sistem yang didukung oleh Bash.

Daniel Kamil Kozar
sumber
59
+1. Kebetulan, Anda bisa mengelabui datemelakukan aritmatika waktu untuk Anda, dengan menulis date -u -d @"$diff" +'%-Mm %-Ss'. (Itu ditafsirkan $diffsebagai detik-sejak-zaman, dan menghitung menit dan detik dalam UTC.) Itu mungkin tidak lagi lebih elegan, meskipun, lebih baik dikaburkan. :-P
ruakh
13
Bagus dan sederhana. Jika seseorang membutuhkan jam:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
chus
6
Ini harus dibaca $(date -u +%s)untuk mencegah kondisi balapan pada tanggal di mana waktu musim panas dihidupkan. Dan waspadai detik-detik lompatan jahat;)
Tino
3
@ daniel-kamil-kozar Nice find. Namun ini agak rapuh. Hanya ada satu variabel seperti itu, sehingga tidak dapat digunakan secara rekursif untuk mengukur kinerja, dan bahkan lebih buruk, setelah unset SECONDSitu hilang:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino
6
@ParthianShot: Saya minta maaf karena Anda pikir begitu. Namun, saya percaya bahwa jawaban ini adalah yang dibutuhkan kebanyakan orang saat mencari jawaban untuk pertanyaan seperti ini: untuk mengukur berapa banyak waktu yang berlalu di antara peristiwa, bukan untuk menghitung waktu.
Daniel Kamil Kozar
99

Detik

Untuk mengukur waktu yang berlalu (dalam detik) kita perlu:

  • bilangan bulat yang mewakili hitungan detik yang berlalu dan
  • cara untuk mengkonversi bilangan bulat tersebut ke format yang dapat digunakan.

Nilai integer detik yang berlalu:

  • Ada dua cara internal bash untuk menemukan nilai integer untuk jumlah detik yang berlalu:

    1. Bash variabel DETIK (jika DETIK tidak disetel itu kehilangan properti khusus).

      • Mengatur nilai DETIK ke 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Menyimpan nilai variabel SECONDSdi awal:

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

Konversikan bilangan bulat tersebut ke format yang dapat digunakan

Bash internal printfdapat melakukannya secara langsung:

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

demikian pula

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

tetapi ini akan gagal untuk durasi lebih dari 24 jam, karena kami benar-benar mencetak waktu jam dinding, bukan durasi:

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

Untuk pecinta detail, dari bash-hackers.org :

%(FORMAT)Tmenghasilkan string tanggal-waktu yang dihasilkan dari menggunakan FORMAT sebagai string format untuk strftime(3). Argumen terkait adalah jumlah detik sejak Epoch , atau -1 (waktu saat ini) atau -2 (waktu startup shell). Jika tidak ada argumen yang sesuai yang diberikan, waktu saat ini digunakan sebagai standar.

Jadi, Anda mungkin ingin menelepon di textifyDuration $elpasedsecondsmana saja textifyDurationimplementasi lain dari pencetakan durasi:

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

Tanggal GNU.

Untuk mendapatkan waktu yang diformat, kita harus menggunakan alat eksternal (tanggal GNU) dalam beberapa cara untuk mendapatkan hingga hampir satu tahun dan termasuk Nanoseconds.

Matematika di dalam tanggal.

Tidak perlu untuk aritmatika eksternal, lakukan semuanya dalam satu langkah di dalam date:

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

Ya, ada 0nol dalam string perintah. Itu dibutuhkan.

Itu dengan asumsi Anda bisa mengubah date +"%T"perintah ke date +"%s"perintah sehingga nilai akan disimpan (dicetak) dalam hitungan detik.

Perhatikan bahwa perintah ini terbatas pada:

  • Nilai positif $StartDatedan $FinalDatedetik.
  • Nilai dalam $FinalDatelebih besar (nanti dalam waktu) daripada $StartDate.
  • Perbedaan waktu lebih kecil dari 24 jam.
  • Anda menerima format output dengan Jam, Menit dan Detik. Sangat mudah diubah.
  • Dapat diterima untuk menggunakan waktu -u UTC. Untuk menghindari "DST" dan koreksi waktu setempat.

Jika Anda harus menggunakan 10:33:56string, yah, ubah saja menjadi detik,
juga, kata detik dapat disingkat menjadi detik:

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"

Perhatikan bahwa konversi waktu detik (seperti yang disajikan di atas) relatif terhadap awal hari "ini" (Hari ini).


Konsepnya dapat diperluas hingga nanodetik, seperti ini:

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"

Jika diharuskan untuk menghitung perbedaan waktu yang lebih lama (hingga 364 hari), kami harus menggunakan awal (beberapa) tahun sebagai referensi dan nilai format %j(jumlah hari dalam tahun):

Mirip dengan:

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

Sayangnya, dalam hal ini, kita perlu secara manual mengurangi 1SATU dari jumlah hari. Perintah tanggal melihat hari pertama tahun ini sebagai 1. Tidak terlalu sulit ...

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



Penggunaan jumlah detik yang lama adalah valid dan didokumentasikan di sini:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Tanggal kotak bus

Alat yang digunakan pada perangkat yang lebih kecil (executable yang sangat kecil untuk diinstal): Busybox.

Buat tautan ke busybox bernama tanggal:

$ ln -s /bin/busybox date

Gunakan kemudian dengan memanggil ini date(letakkan di direktori PATH termasuk).

Atau buat alias seperti:

$ alias date='busybox date'

Tanggal Busybox memiliki opsi yang bagus: -D untuk menerima format waktu input. Itu membuka banyak format untuk digunakan sebagai waktu. Menggunakan opsi -D kita dapat mengonversi waktu 10:33:56 secara langsung:

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

Dan seperti yang Anda lihat dari output Komando di atas, hari itu dianggap "hari ini". Untuk mendapatkan waktu mulai dari zaman:

$ 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

Tanggal Busybox bahkan dapat menerima waktu (dalam format di atas) tanpa -D:

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

Dan format output bahkan bisa menjadi detik sejak zaman.

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

Untuk kedua kali, dan sedikit matematika bash (busybox belum dapat melakukan matematika, belum):

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

Atau diformat:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
David Tonhofer
sumber
6
Ini adalah jawaban yang luar biasa meliputi banyak pangkalan dan dengan penjelasan yang bagus. Terima kasih!
Devy
Saya harus mencari %(FORMAT)Tstring yang diteruskan ke printfperintah yang terintegrasi dalam bash. Dari bash-hackers.org : %(FORMAT)Toutput string tanggal-waktu yang dihasilkan dari penggunaan FORMATsebagai string format untuk strftime (3). Argumen terkait adalah jumlah detik sejak Epoch, atau -1 (waktu saat ini) atau -2 (waktu startup shell). Jika tidak ada argumen yang sesuai adalah persediaan, waktu saat ini digunakan sebagai standar . Itu esoteris. Dalam hal ini, %ssebagaimana diberikan strftime(3)adalah "jumlah detik sejak zaman".
David Tonhofer
35

Inilah cara saya melakukannya:

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

Sangat sederhana, ambil jumlah detik di awal, lalu ambil jumlah detik di akhir, dan cetak perbedaan dalam menit: detik.

Dorian
sumber
6
Apa bedanya dengan solusi saya? Saya benar-benar tidak dapat melihat manfaat dari memanggil awkdalam kasus ini, karena Bash menangani bilangan aritmatika sama baiknya.
Daniel Kamil Kozar
1
Jawaban Anda juga benar. Beberapa orang, seperti saya, lebih suka bekerja dengan awk daripada dengan bash inconsistencies.
Dorian
2
Bisakah Anda menguraikan lebih lanjut tentang inkonsistensi dalam Bash tentang aritmatika integer? Saya ingin tahu lebih banyak tentang ini, karena saya tidak menyadarinya.
Daniel Kamil Kozar
12
Saya hanya mencari ini. Saya tidak mengerti kritik atas jawaban ini. Saya suka melihat lebih dari satu solusi untuk suatu masalah. Dan, saya salah satu yang lebih suka awkperintah untuk bash (jika tidak untuk yang lain, karena awk bekerja di shell lain). Saya lebih menyukai solusi ini. Tapi itu pendapat pribadi saya.
rpsml
4
Angka nol utama: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Jon Strayer
17

Pilihan lain adalah menggunakan datediffdari 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

Anda juga bisa menggunakannya gawk. mawk1.3.4 juga memiliki strftimedan mktimetetapi versi yang lebih lama mawkdan nawktidak.

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

Atau inilah cara lain untuk melakukannya dengan 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
sumber
1
Saya mencari sesuatu seperti date metode GNU Anda . Jenius.
Haohmaru
Silakan hapus komentar saya ... maaf
Bill Gale
Setelah menginstal dateutilsmelalui apt, tidak ada datediffperintah - harus digunakan dateutils.ddiff.
Suzana
12

Saya ingin mengusulkan cara lain yang menghindari mengingat dateperintah. Mungkin bermanfaat jika Anda telah mengumpulkan cap waktu dalam %Tformat tanggal:

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"

kita bahkan dapat menangani milidetik dengan cara yang sama.

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
sumber
1
Apakah ada cara untuk menangani milidetik, mis. 10: 33: 56.104
Umesh Rajbhandari
Milidetik menangani perilaku buruk jika bidang milidetik dimulai dengan nol. Misalnya ms = "033" akan memberikan digit tambahan "027" karena bidang ms ditafsirkan sebagai oktal. mengubah "echo" ... "+ ms))" menjadi ... "+ $ {ms ## + (0)}))" akan memperbaikinya selama "shopt -s extglob" muncul di suatu tempat di atas ini di naskah. Seseorang mungkin harus menghapus nol terkemuka dari h, m, dan juga ...
Eric Towers
3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))bekerja untuk saya sejak saya dapatkanvalue too great for base (error token is "08")
Niklas
6

Inilah beberapa keajaiban:

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

sedmengganti a :dengan formula untuk dikonversi ke 1/60. Kemudian perhitungan waktu yang dilakukan olehbc

harum
sumber
5

Sampai saat ini (GNU coreutils) 7.4 Anda sekarang dapat menggunakan -d untuk melakukan aritmatika:

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

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

Unit yang dapat Anda gunakan adalah hari, tahun, bulan, jam, menit, dan detik:

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

Sebagai lanjutan dari jawaban Daniel Kamil Kozar, untuk menunjukkan jam / menit / detik:

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

Jadi skrip lengkapnya adalah:

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

Atau bungkus sedikit

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

Maka ini bekerja.

timerstart; sleep 2; timerstop
seconds=2
pengguna3561136
sumber
3

Berikut adalah solusi hanya menggunakan datekemampuan perintah menggunakan "lalu", dan tidak menggunakan variabel kedua untuk menyimpan waktu selesai:

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

ini memberi:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120
Zoltan K.
sumber
2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds
Yusuf
sumber
6
Akan sangat membantu untuk mengetahui apa yang dilakukan jawaban ini yang tidak dilakukan oleh jawaban yang lain.
Louis
Ini memungkinkan Anda dengan mudah menampilkan durasi dalam format apa pun yang diizinkan oleh date.
Ryan C. Thompson
2

date dapat memberi Anda perbedaan dan memformatnya untuk Anda (opsi OS X ditampilkan)

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

Beberapa pemrosesan string dapat menghapus nilai-nilai kosong itu

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

Ini tidak akan berfungsi jika Anda menempatkan waktu lebih dulu. Jika Anda perlu mengatasinya, ubah $(($(date ...) - $(date ...)))ke$(echo $(date ...) - $(date ...) | bc | tr -d -)

Dave
sumber
1

Saya membutuhkan skrip perbedaan waktu untuk digunakan dengan mencoder( --endposrelatif), dan solusi saya adalah memanggil skrip Python:

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

fraksi detik juga didukung:

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

dan dapat memberitahu Anda bahwa perbedaan antara 200 dan 120 adalah 1j 20m:

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

dan dapat mengubah jumlah (mungkin pecahan) detik atau menit atau jam menjadi jj: mm: dd

$ ./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
sumber
1

Dengan 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
pengguna5207022
sumber
1

Generalisasi solusi @ nisetama menggunakan tanggal GNU (Ubuntu 14.04 LTS) yang dapat dipercaya:

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

echo $start
echo $stop
echo $duration

menghasilkan:

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

Tentukan fungsi ini (katakan dalam ~ / .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
}

Sekarang Anda dapat mengukur waktu bagian dari skrip Anda:

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

$ ./script.sh
Total time: 0.5060

sangat berguna untuk menemukan bottlenecks eksekusi.

sergio
sumber
0

Saya menyadari ini adalah posting yang lebih tua, tetapi saya menemukan hari ini ketika mengerjakan sebuah skrip yang akan mengambil tanggal dan waktu dari file log dan menghitung delta. Script di bawah ini pasti berlebihan, dan saya sangat merekomendasikan memeriksa logika dan matematika saya.

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

Anda akan mendapatkan output sebagai berikut.

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.

Saya memodifikasi skrip sedikit untuk membuatnya mandiri (mis. Hanya mengatur nilai variabel), tapi mungkin ide umum juga muncul. Anda mungkin ingin beberapa kesalahan tambahan memeriksa nilai negatif.

Daniel Blackmon
sumber
Astaga, banyak pekerjaan !!. Silakan lihat jawaban saya: stackoverflow.com/a/24996647/2350426
0

Saya tidak dapat mengomentari jawaban mcaleaa, maka saya memposting ini di sini. Variabel "diff" harus dalam huruf kecil. Berikut ini sebuah contoh.

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

Variabel sebelumnya dinyatakan dalam huruf kecil. Inilah yang terjadi ketika huruf besar digunakan.

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

Jadi, perbaikan cepat akan seperti ini

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

Inilah implementasi bash saya (dengan bit diambil dari SO lainnya ;-)

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

Tidak diuji, mungkin bermasalah.

Ondra Žižka
sumber