Làm thế nào để tính thời gian trôi qua trong tập lệnh bash?


224

Tôi in thời gian bắt đầu và kết thúc bằng cách sử dụng date +"%T", kết quả là:

10:33:56
10:36:10

Làm thế nào tôi có thể tính toán và in sự khác biệt giữa hai?

Tôi muốn nhận được một cái gì đó như:

2m 14s

5
Trên một lưu ý khác, bạn có thể không sử dụng timelệnh?
anishsane

Sử dụng thời gian unix thay vào đó, date +%ssau đó trừ đi để có sự khác biệt trong vài giây.
roblogic

Câu trả lời:


476

Bash có một SECONDSbiến dựng sẵn tiện dụng theo dõi số giây đã trôi qua kể từ khi vỏ được bắt đầu. Biến này giữ lại các thuộc tính của nó khi được gán và giá trị được trả về sau khi gán là số giây kể từ khi gán cộng với giá trị được gán.

Do đó, bạn chỉ có thể đặt SECONDSthành 0 trước khi bắt đầu sự kiện hẹn giờ, chỉ cần đọc SECONDSsau sự kiện và thực hiện số học thời gian trước khi hiển thị.

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

Vì giải pháp này không phụ thuộc vào date +%s(là phần mở rộng GNU), nên nó có thể di động tới tất cả các hệ thống được Bash hỗ trợ.


59
+1. Ngẫu nhiên, bạn có thể lừa dateđể thực hiện số học thời gian cho bạn, bằng cách viết date -u -d @"$diff" +'%-Mm %-Ss'. (Điều đó diễn giải $diffdưới dạng giây kể từ giây, và tính toán số phút và giây trong UTC.) Điều đó có lẽ không còn thanh lịch hơn, mặc dù vậy, chỉ bị che khuất tốt hơn. :-P
ruakh

13
Đẹp và đơn giản. Nếu ai đó cần giờ:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
chus

6
Nó nên đọc $(date -u +%s)để ngăn chặn tình trạng cuộc đua vào những ngày mà thời gian tiết kiệm ánh sáng ban ngày được chuyển đổi. Và hãy cẩn thận với những giây nhuận độc ác;)
Tino

3
@ daniel-kamil-kozar Đẹp tìm. Tuy nhiên điều này là một chút mong manh. Chỉ có một biến như vậy, vì vậy nó không thể được sử dụng đệ quy để đo hiệu suất, và thậm chí tệ hơn, sau khi unset SECONDSnó biến mất:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino

6
@ParthianShot: Tôi xin lỗi vì bạn nghĩ vậy. Tuy nhiên, tôi tin rằng câu trả lời này là điều mà hầu hết mọi người cần trong khi tìm kiếm câu trả lời cho câu hỏi như thế này: để đo thời gian giữa các sự kiện, không tính thời gian.
Daniel Kamil Kozar

99

Giây

Để đo thời gian trôi qua (tính bằng giây), chúng ta cần:

  • một số nguyên biểu thị số giây đã trôi qua và
  • một cách để chuyển đổi số nguyên như vậy sang một định dạng có thể sử dụng.

Một giá trị nguyên của giây trôi qua:

  • Có hai cách nội bộ bash để tìm giá trị nguyên cho số giây đã trôi qua:

    1. Bash biến SECONDS (nếu SECONDS không được đặt thì nó sẽ mất thuộc tính đặc biệt).

      • Đặt giá trị của GIÂY thành 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Lưu trữ giá trị của biến SECONDSkhi bắt đầu:

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
    2. Tùy chọn in Bash %(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 ))

Chuyển đổi số nguyên như vậy sang định dạng có thể sử dụng

Bash nội bộ printfcó thể làm điều đó trực tiếp:

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

tương tự

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

nhưng điều này sẽ thất bại trong thời gian hơn 24 giờ, vì chúng tôi thực sự in thời gian trên đồng hồ, không thực sự là thời lượng:

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

Dành cho những người yêu thích chi tiết, từ bash-hackers.org :

%(FORMAT)Txuất chuỗi thời gian ngày kết quả từ việc sử dụng FORMAT làm chuỗi định dạng cho strftime(3). Đối số được liên kết là số giây kể từ Epoch hoặc -1 (thời gian hiện tại) hoặc -2 (thời gian khởi động hệ vỏ). Nếu không có đối số tương ứng được cung cấp, thời gian hiện tại được sử dụng làm mặc định.

Vì vậy, bạn có thể chỉ muốn gọi textifyDuration $elpasedsecondsnơi textifyDurationthực hiện in thời lượng khác:

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

Ngày GNU.

Để có được thời gian hình thành, chúng ta nên sử dụng một công cụ bên ngoài (ngày GNU) theo nhiều cách để có thời lượng lên tới gần một năm và bao gồm cả Nanoseconds.

Toán bên trong ngày.

Không cần số học bên ngoài, hãy làm tất cả trong một bước bên trong date:

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

Vâng, có một 0số không trong chuỗi lệnh. Nó là cần thiết.

Giả sử bạn có thể thay đổi date +"%T"lệnh thành date +"%s"lệnh để các giá trị sẽ được lưu trữ (in) sau vài giây.

Lưu ý rằng lệnh được giới hạn ở:

  • Giá trị dương $StartDate$FinalDategiây.
  • Giá trị trong $FinalDatelớn hơn (về sau) $StartDate.
  • Thời gian chênh lệch nhỏ hơn 24 giờ.
  • Bạn chấp nhận một định dạng đầu ra với Giờ, Phút và Giây. Rất dễ thay đổi.
  • Có thể chấp nhận sử dụng -u lần UTC. Để tránh "DST" và hiệu chỉnh thời gian cục bộ.

Nếu bạn phải sử dụng 10:33:56chuỗi, chỉ cần chuyển đổi nó thành giây,
đồng thời, từ giây có thể được viết tắt là giây:

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"

Lưu ý rằng chuyển đổi thời gian giây (như được trình bày ở trên) có liên quan đến ngày bắt đầu của "ngày này" (Hôm nay).


Khái niệm này có thể được mở rộng đến nano giây, như thế này:

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"

Nếu được yêu cầu tính chênh lệch thời gian dài hơn (tối đa 364 ngày), chúng ta phải sử dụng bắt đầu (một số) năm làm tham chiếu và giá trị định dạng %j(số ngày trong năm):

Tương tự như:

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

Đáng buồn thay, trong trường hợp này, chúng ta cần trừ 1MỘT cách thủ công khỏi số ngày. Lệnh date xem ngày đầu tiên của năm là 1. Không khó lắm ...

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



Việc sử dụng số giây dài là hợp lệ và được ghi lại ở đây:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Ngày bận rộn

Một công cụ được sử dụng trong các thiết bị nhỏ hơn (một tệp thực thi rất nhỏ để cài đặt): Busybox.

Tạo liên kết đến busybox được gọi là ngày:

$ ln -s /bin/busybox date

Sử dụng nó sau đó bằng cách gọi nó date(đặt nó trong một thư mục bao gồm PATH).

Hoặc tạo một bí danh như:

$ alias date='busybox date'

Ngày Busybox có một tùy chọn đẹp: -D để nhận định dạng của thời gian nhập. Điều đó mở ra rất nhiều định dạng được sử dụng như thời gian. Sử dụng tùy chọn -D, chúng ta có thể chuyển đổi trực tiếp thời gian 10:33:56:

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

Và như bạn có thể thấy từ đầu ra của Lệnh ở trên, ngày được coi là "hôm nay". Để có được thời gian bắt đầu trên epoch:

$ 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

Ngày Busybox thậm chí có thể nhận được thời gian (ở định dạng trên) mà không có -D:

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

Và định dạng đầu ra thậm chí có thể là vài giây kể từ epoch.

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

Đối với cả hai lần và một chút bash math (busybox chưa thể làm toán):

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

Hoặc được định dạng:

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

6
Đây là một câu trả lời tuyệt vời bao gồm rất nhiều cơ sở và với lời giải thích tốt. Cảm ơn bạn!
Devy

Tôi đã phải tìm %(FORMAT)Tchuỗi được truyền cho printflệnh được tích hợp trong bash. Từ bash-hackers.org : %(FORMAT)Txuất chuỗi thời gian ngày kết quả từ việc sử dụng FORMATlàm chuỗi định dạng cho strftime (3). Đối số được liên kết là số giây kể từ Epoch hoặc -1 (thời gian hiện tại) hoặc -2 (thời gian khởi động hệ vỏ). Nếu không có đối số tương ứng là nguồn cung cấp, thời gian hiện tại được sử dụng làm mặc định . Đó là bí truyền. Trong trường hợp này, %snhư được đưa ra strftime(3)là "số giây kể từ kỷ nguyên".
David Tonhofer

35

Đây là cách tôi đã làm nó:

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

Thực sự đơn giản, lấy số giây khi bắt đầu, sau đó lấy số giây ở cuối và in chênh lệch tính bằng phút: giây.


6
Làm thế nào là khác với giải pháp của tôi? Tôi thực sự không thể thấy được lợi ích từ việc gọi awktrong trường hợp này, vì Bash xử lý số học số nguyên tốt như nhau.
Daniel Kamil Kozar

1
Câu trả lời của bạn cũng đúng. Một số người, như tôi, thích làm việc với awk hơn là với sự không nhất quán bash.
Dorian

2
Bạn có thể giải thích thêm về sự không nhất quán trong Bash liên quan đến số học số nguyên không? Tôi muốn biết thêm về điều này, vì tôi không biết gì về nó.
Daniel Kamil Kozar

12
Tôi chỉ tìm kiếm cái này Tôi không hiểu những lời chỉ trích cho câu trả lời này. Tôi muốn thấy nhiều hơn một giải pháp cho một vấn đề. Và, tôi là một người thích awkcác lệnh bash (nếu không có gì khác, vì awk hoạt động trong các shell khác). Tôi thích giải pháp này tốt hơn. Nhưng đó là ý kiến ​​cá nhân của tôi.
rpsml

4
Các số 0 đứng đầu: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Jon Strayer

17

Một tùy chọn khác là sử dụng datedifftừ 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

Bạn cũng có thể sử dụng gawk. mawk1.3.4 cũng có strftimemktimenhưng phiên bản cũ hơn mawknawkkhông.

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

Hoặc đây là một cách khác để làm điều đó với 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

1
Tôi đang tìm kiếm một cái gì đó giống như date phương pháp GNU của bạn . Thiên tài.
Haohmaru

Vui lòng xóa nhận xét của tôi ... xin lỗi
Bill Gale

Sau khi cài đặt dateutilsqua apt, không có datedifflệnh - phải sử dụng dateutils.ddiff.
Suzana

12

Tôi muốn đề xuất một cách khác để tránh thu hồi datelệnh. Nó có thể hữu ích trong trường hợp nếu bạn đã thu thập dấu thời gian ở %Tđịnh dạng ngày:

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"

chúng ta thậm chí có thể xử lý mili giây theo cùng một cách.

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
Có cách nào để xử lý millisecons không, ví dụ 10: 33: 56.104
Umesh Rajbhandari

Millisecond xử lý các hành vi sai nếu trường mili giây bắt đầu bằng 0. Ví dụ ms = "033" sẽ đưa ra các chữ số "027" vì trường ms đang được hiểu là bát phân. thay đổi "echo" ... "+ ms))" thành ... "+ $ {ms ## + (0)}))" sẽ khắc phục điều này miễn là "shopt -s extglob" xuất hiện ở đâu đó phía trên này trong kịch bản. Có lẽ mọi người cũng nên loại bỏ các số 0 đứng đầu từ h, m và s ...
Eric Towers

3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))làm việc cho tôi kể từ khi tôi nhận đượcvalue too great for base (error token is "08")
Niklas

6

Đây là một số phép thuật:

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

sedthay thế một :bằng một công thức để chuyển đổi thành 1/60. Sau đó, tính toán thời gian được thực hiện bởibc


5

Kể từ ngày (GNU coreutils) 7.4 bây giờ bạn có thể sử dụng -d để thực hiện số học:

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

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

Các đơn vị bạn có thể sử dụng là ngày, năm, tháng, giờ, phút và giây:

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

3

Theo dõi câu trả lời của Daniel Kamil Kozar, để hiển thị giờ / phút / giây:

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

Vì vậy, kịch bản đầy đủ sẽ là:

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

3

Hoặc gói lại một chút

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

Sau đó, điều này hoạt động.

timerstart; sleep 2; timerstop
seconds=2

3

Đây là một giải pháp chỉ sử dụng datecác khả năng của lệnh bằng cách sử dụng "trước" và không sử dụng biến thứ hai để lưu trữ thời gian kết thúc:

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

Điều này mang lại:

$ ./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
Sẽ rất hữu ích khi biết câu trả lời này làm gì mà các câu trả lời khác không làm.
Louis

Nó cho phép bạn dễ dàng xuất thời lượng ở bất kỳ định dạng nào được cho phép date.
Ryan C. Thompson

2

date có thể cung cấp cho bạn sự khác biệt và định dạng nó cho bạn (tùy chọn OS X được hiển thị)

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

Một số xử lý chuỗi có thể loại bỏ các giá trị trống

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

Điều này sẽ không hoạt động nếu bạn đặt thời gian sớm hơn trước. Nếu bạn cần xử lý việc đó, hãy đổi $(($(date ...) - $(date ...)))thành$(echo $(date ...) - $(date ...) | bc | tr -d -)


1

Tôi cần một tập lệnh chênh lệch thời gian để sử dụng với mencoder(nó --endposlà tương đối) và giải pháp của tôi là gọi một tập lệnh Python:

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

phân số của giây cũng được hỗ trợ:

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

và nó có thể cho bạn biết rằng sự khác biệt giữa 200 và 120 là 1h 20m:

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

và có thể chuyển đổi bất kỳ (có thể là phân số) số giây hoặc phút hoặc giờ sang 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])

1

Với 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

Tổng quát hóa giải pháp của @ nisetama bằng ngày GNU (Ubuntu 14.04 LTS đáng tin cậy):

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

echo $start
echo $stop
echo $duration

năng suất:

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

Xác định hàm này (giả sử trong ~ / .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
}

Bây giờ bạn có thể đo thời gian của các phần trong tập lệnh của mình:

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

$ ./script.sh
Total time: 0.5060

rất hữu ích để tìm ra tắc nghẽn thực thi.


0

Tôi nhận ra đây là một bài viết cũ hơn, nhưng tôi đã xem nó ngày hôm nay trong khi làm việc với một kịch bản sẽ lấy ngày và thời gian từ một tệp nhật ký và tính toán delta. Kịch bản dưới đây chắc chắn là quá mức cần thiết, và tôi khuyên bạn nên kiểm tra logic và toán học của mình.

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

Bạn sẽ nhận được đầu ra như sau.

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.

Tôi đã sửa đổi tập lệnh một chút để làm cho nó độc lập (nghĩa là chỉ đặt các giá trị biến), nhưng có lẽ ý tưởng chung cũng xuất hiện. Bạn có thể muốn một số kiểm tra lỗi bổ sung cho các giá trị âm.


Trời ạ, rất nhiều việc !! Hãy nhìn vào câu trả lời của tôi: stackoverflow.com/a/24996647/2350426

0

Tôi không thể nhận xét về câu trả lời của mcaleaa, do đó tôi đăng bài này ở đây. Biến "diff" phải nằm trong trường hợp nhỏ. Đây là một ví dụ.

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

Biến trước được khai báo bằng chữ thường Đây là những gì đã xảy ra khi chữ hoa được sử dụng.

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

Vì vậy, sửa chữa nhanh chóng sẽ như thế này

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

-1

Đây là cách thực hiện bash của tôi (với các bit được lấy từ SO khác ;-)

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

Không được kiểm tra, có thể có lỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.