Як обчислити час, що минув у скрипті bash?


224

Я друкую час початку та закінчення за допомогою date +"%T", що призводить до чогось такого типу:

10:33:56
10:36:10

Як я міг обчислити та роздрукувати різницю між цими двома?

Я хотів би отримати щось на кшталт:

2m 14s

5
З іншого приводу, чи не могли ви використовувати timeкоманду?
anishsane

Замість цього використовуйте час Unix, а date +%sпотім віднімайте, щоб отримати різницю в секундах.
roblogic

Відповіді:


476

Баш корисний SECONDS вбудовану змінну, яка відстежує кількість секунд, що пройшли з моменту запуску оболонки. Ця змінна зберігає свої властивості при призначенні, а значення, яке повертається після призначення, - це кількість секунд з моменту присвоєння плюс присвоєне значення.

Таким чином, ви можете просто встановити SECONDS0 перед початком події, приуроченої до цього часу, просто прочитати SECONDSпісля події та виконати арифметику часу перед відображенням.

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

Оскільки це рішення не залежить від date +%sрозширення GNU, воно є портативним для всіх систем, що підтримуються Bash.


59
+1. Між іншим, ви можете підманути dateвиконання арифметики часу, написавши date -u -d @"$diff" +'%-Mm %-Ss'. (Це трактується $diffяк секунди з часу епохи, і обчислює хвилини та секунди в UTC.) Мабуть, це не є більш елегантним, хоча просто краще заплутане. :-P
ruakh

13
Приємно і просто. Якщо кому - то потрібно годинник:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
Chus

6
Він повинен читати, $(date -u +%s)щоб запобігти умовам перегонів у дати, коли перемикає літній час. І остерігайтеся злих стрибків секунд;)
Тіно

3
@ daniel-kamil-kozar Приємна знахідка. Однак це трохи крихко. Є лише одна така змінна, тому її не можна використовувати рекурсивно для вимірювання продуктивності, а ще гірше - після unset SECONDSїї відсутності :echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino

6
@ParthianShot: Мені шкода, що ти так вважаєш. Однак я вважаю, що ця відповідь - це те, що потрібно більшості людей під час пошуку відповідей на таке запитання: вимірювати скільки часу пройшло між подіями, а не обчислювати час.
Даніель Каміль Козар

99

Секунди

Для вимірювання минулого часу (у секундах) нам потрібно:

  • ціле число, яке представляє кількість пройдених секунд і
  • спосіб конвертувати таке ціле число у придатний формат.

Ціле значення минулих секунд:

  • Існує два внутрішні способи bash, щоб знайти ціле значення за кількість минулих секунд:

    1. Змінна Bash SECONDS (якщо значення SECONDS не встановлено, воно втрачає особливу властивість).

      • Встановлення значення SECONDS до 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Збереження значення змінної SECONDSна початку:

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
    2. Варіант 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 ))

Перетворити таке ціле число у придатний формат

Баш внутрішній printfможе це зробити безпосередньо:

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

аналогічно

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

але це не вдасться тривалістю понад 24 години, оскільки ми насправді друкуємо час настінних годин, а не насправді тривалість:

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

Для любителів деталей, з bash-hackers.org :

%(FORMAT)Tвиводить рядок дати-часу в результаті використання FORMAT як рядка формату для strftime(3). Асоційованим аргументом є кількість секунд після Epoch , або -1 (поточний час) або -2 (час запуску оболонки). Якщо не вказано відповідний аргумент, за замовчуванням використовується поточний час.

Тому ви можете просто зателефонувати, textifyDuration $elpasedsecondsде textifyDurationще одна реалізація тривалості друку:

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

Для отримання формалізованого часу нам слід використовувати зовнішній інструмент (дата GNU) декількома способами, щоб досягти майже року, включаючи наносекунди.

Математика всередині побачення.

Зовнішня арифметика не потрібна, зробіть це все за один крок всередині date:

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

Так, є 0 у командному рядку нуль. Це потрібно.

Це припускаючи, що ви можете змінити date +"%T"команду на adate +"%s" команду, щоб значення зберігалися (друкувалися) у секундах.

Зауважте, що команда обмежена:

  • Позитивні значення $StartDateі $FinalDateсекунди.
  • Значення в $FinalDateє більшим (пізніший час), ніж $StartDate.
  • Різниця в часі менше 24 годин.
  • Ви приймаєте формат виводу за допомогою годин, хвилин і секунд. Дуже легко змінити.
  • Прийнятно використовувати -у UTC раз. Щоб уникнути "DST" та місцевих корекцій часу.

Якщо ви повинні використовувати 10:33:56рядок, ну просто перетворіть його на секунди,
також слово секунди може бути скорочено як 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"

Зауважимо, що перетворення часу в секундах (як представлено вище) відносно початку "цього" дня (сьогодні).


Концепція може бути розширена до наносекунд, наприклад:

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"

Якщо потрібно обчислити більш тривалі (до 364 днів) різниці у часі, ми повинні використовувати початок (деякого) року як орієнтир та значення формату %j(число дня в році):

Схожий на:

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

На жаль, у цьому випадку нам потрібно вручну відняти 1ОДНО від кількості днів. Команда дати побачить перший день року як 1. Не так вже й складно ...

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



Використання тривалої кількості секунд є дійсним і задокументовано тут:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Дата зайнятості

Інструмент, що використовується на менших пристроях (дуже малий виконуваний файл для встановлення): Busybox.

Або зробіть посилання на зайняту скриньку під назвою дата:

$ ln -s /bin/busybox date

Використовуйте його, зателефонувавши до цього date(помістіть його у каталог, включений у PATH).

Або зробіть псевдонім на зразок:

$ alias date='busybox date'

Дата зайнятої скриньки має хороший варіант: -D отримати формат часу введення. Це відкриває багато форматів, які використовуються як час. Використовуючи опцію -D, ми можемо перетворити час 10:33:56 безпосередньо:

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

І як видно з результатів командування вище, день вважається "сьогодні". Щоб отримати час, починаючи з епохи:

$ 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

Дата зайнятої скриньки навіть може отримати час (у форматі вище) без -D:

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

А вихідний формат міг би навіть секунди з епохи.

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

І для обох разів, і для невеликої математики (зайнятий ще не може зробити математику):

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

Або відформатовано:

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

6
Ця така дивовижна відповідь охопила безліч підстав і тонких пояснень. Дякую!
Devy

Мені довелося шукати %(FORMAT)Tрядок, переданий printfкоманді, інтегрованій у bash. Від bash-hackers.org : %(FORMAT)Tвиведіть рядок дати та часу, що випливає з використання FORMATв якості рядка формату для стропового переривання (3). Асоційованим аргументом є кількість секунд після Epoch, або -1 (поточний час) або -2 (час запуску оболонки). Якщо відповідний аргумент не надається, то за замовчуванням використовується поточний час . Це езотерика. У цьому випадку, %sяк дано, strftime(3)це "кількість секунд з епохи".
Девід Тонхофер

35

Ось як я це зробив:

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

По-справжньому просто, візьміть кількість секунд на старті, потім візьміть кількість секунд в кінці і роздрукуйте різницю в хвилинах: секунд.


6
Чим це відрізняється від мого рішення? Я дійсно не бачу вигоди від виклику awkв цьому випадку, оскільки Bash однаково добре обробляє цілу арифметику.
Даніель Каміль Козар

1
Ваша відповідь також правильна. Деякі люди, як я, вважають за краще працювати з awk, ніж з bash невідповідностями.
Доріан

2
Не могли б ви детальніше розповісти про невідповідності Баша щодо цілої арифметики? Я хотів би дізнатися більше про це, оскільки я не знав про це.
Даніель Каміль Козар

12
Я просто шукав цього. Я не розумію критики до цієї відповіді. Мені подобається бачити не одне рішення проблеми. І я - той, хто вважає за краще awkкоманди командувати башти (якщо нічого іншого, тому що awk працює в інших оболонках). Мені це рішення сподобалось більше. Але це моя особиста думка.
rpsml

4
Провідні нулі: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Джон Страйер

17

Іншим варіантом є використання datediffз dateutils( http://www.fresse.org/dateutils/#dateiff ):

$ 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

Ви також можете використовувати gawk. mawk1.3.4 також має strftimeі mktimeале старіші версії mawkі nawkне роблять.

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

Або ось ще один спосіб зробити це з 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
Я шукав щось на зразок вашого date методу GNU . Геніальність.
Haohmaru

Видаліть мій коментар ... вибачте
Білл Гейл

Після установки dateutilsчерез apt не було datediffкоманди - довелося використовувати dateutils.ddiff.
Сузана

12

Я хотів би запропонувати інший спосіб, який уникає відкликання dateкоманди. Це може бути корисно у випадку, якщо ви вже зібрали часові позначки у %Tформаті дати:

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"

ми навіть можемо поводитися з мілісекундами таким же чином.

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
Чи є спосіб поводження з мілісексуалами, наприклад, 10: 33: 56.104
Умеш

Мілісекунда поводиться недобре, якщо мілісекундне поле починається з нуля. Наприклад, ms = "033" дасть кінцеві цифри "027", оскільки поле ms інтерпретується як восьмеричний. змінивши "echo" ... "+ ms))" на ... "+ $ {ms ## + (0)}))" це виправить до тих пір, поки "shopt -s extglob" з'явиться десь над цим у сценарій. Напевно, слід позбутися провідних нулів від h, m та s також ...
Ерік Тауерс

3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))працював на мене з мого отриманняvalue too great for base (error token is "08")
Ніклас

6

Ось кілька чарів:

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

sedзамінює :формулу для перетворення на 1/60. Тоді підрахунок часу, який робитьсяbc


5

Станом на дату (GNU coreutils) 7.4, тепер ви можете використовувати -d для виконання арифметики:

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

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

Одиницями, якими ви можете скористатися, є дні, роки, місяці, години, хвилини та секунди:

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

3

Після відповіді Даніеля Каміля Козара, щоб показати години / хвилини / секунди:

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

Тож повний сценарій був би:

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

3

Або трохи загорнути

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

Тоді це працює.

timerstart; sleep 2; timerstop
seconds=2

3

Ось рішення, що використовує лише dateможливості команд, що використовують "назад", а не використання другої змінної для зберігання часу закінчення:

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

це дає:

$ ./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
Було б корисно знати, що робить ця відповідь, що інші відповіді не роблять.
Луї

Це дозволяє легко виводити тривалість у будь-якому форматі, дозволеному date.
Райан Ч. Томпсон

2

date може дати вам різницю та відформатувати її (відображені параметри OS X)

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

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

Деяка обробка рядків може видалити ці порожні значення

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

Це не спрацює, якщо ви поставите його раніше. Якщо вам потрібно впоратися з цим, перейдіть $(($(date ...) - $(date ...)))на$(echo $(date ...) - $(date ...) | bc | tr -d -)


1

Мені потрібен був сценарій різниці у часі для використання з mencoder(його --endposвідносно), і моє рішення - викликати сценарій Python:

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

також підтримуються частки секунд:

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

і це може сказати вам, що різниця між 200 і 120 становить 1h 20m:

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

і може перетворити будь-яке (можливо, дробове) число секунд або хвилин або годин у год: мм: сс

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

З 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

Узагальнення рішення @ nisetama з використанням дати GNU (надійний 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

врожайність:

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

Визначте цю функцію (скажіть у ~ / .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
}

Тепер ви можете виміряти час частини ваших сценаріїв:

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

$ ./script.sh
Total time: 0.5060

дуже корисно знайти вузькі місця виконання.


0

Я розумію, що це старіший пост, але я натрапив на нього сьогодні, працюючи над сценарієм, який братиме дати та часи з файлу журналу та обчислює дельту. Наведений нижче сценарій, безумовно, є надмірним, і я настійно рекомендую перевірити свою логіку та математику.

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

Ви отримаєте вихід таким чином.

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.

Я трохи змінив сценарій, щоб зробити його самостійним (тобто просто встановити змінні значення), але, можливо, загальна ідея також зустрічається. Вам може знадобитися додаткова перевірка помилок на наявність негативних значень.


Гей, багато роботи !!. Будь ласка , подивіться на мою відповідь: stackoverflow.com/a/24996647/2350426

0

Я не можу коментувати відповідь mcaleaa, тому я публікую це тут. Змінна "diff" повинна бути в малому регістрі. Ось приклад.

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

Попередня змінна була оголошена в нижньому регістрі. Це те, що сталося, коли використовується велика літера.

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

Отже, швидке виправлення було б таким

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

-1

Ось моя реалізація bash (з бітами, взятими з інших 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

Не тестується, може бути баггі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.