Как рассчитать время, прошедшее в bash-скрипте?

224

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

10:33:56
10:36:10

Как я могу рассчитать и распечатать разницу между этими двумя?

Я хотел бы получить что-то вроде:

2m 14s
Миша Морошко
источник
5
С другой стороны, не могли бы вы использовать timeкоманду?
Anishsane
Вместо этого используйте unix time date +%s, затем вычтите, чтобы получить разницу в секундах.
roblogic

Ответы:

476

Bash имеет удобную 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."
Ча
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. Опция 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 ))

Преобразовать такое целое число в пригодный для использования формат

Внутренний bash 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). Связанный аргумент - это количество секунд с начала эпохи , или -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:

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

Да, 0в командной строке есть ноль. Необходимо.

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

Обратите внимание, что команда ограничена:

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

Если вы должны использовать 10:33:56строку, ну, просто преобразуйте ее в секунды,
также, слово секунд может быть сокращено как сек:

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ОДИН из числа дней. Команда date рассматривает первый день года как 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.

Либо сделайте ссылку на busybox с названием date:

$ ln -s /bin/busybox date

Затем используйте его, вызвав его date(поместите в каталог, включенный в PATH).

Или сделайте псевдоним как:

$ alias date='busybox date'

У даты Busybox есть приятная опция: -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

Дата Busybox может даже получить время (в формате выше) без -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

Для обоих раз и немного bash math (busybox не может сделать математику, пока):

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
Это такой удивительный ответ, охватывающий множество оснований и прекрасных объяснений. Спасибо!
Деви
Мне пришлось искать %(FORMAT)Tстроку, переданную printfкоманде, встроенной в bash. От bash-hackers.org : %(FORMAT)Tвывести строку даты и времени, полученную в результате использования FORMATв качестве строки формата для strftime (3). Связанный аргумент - это количество секунд с начала эпохи, или -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
Не могли бы вы подробнее рассказать о несоответствиях в Bash относительно целочисленной арифметики? Я хотел бы знать больше об этом, так как я не знал ни о чем.
Даниэль Камил Козар
12
Я просто искал это. Я не понимаю критику этого ответа. Мне нравится видеть больше, чем одно решение проблемы. И я - тот, кто предпочитает awkкоманды bash (если ни для чего другого, потому что 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/#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

Вы также можете использовать 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
nisetama
источник
1
Я искал что-то вроде вашего date метода GNU . Genius.
Хаоммару
Пожалуйста, удалите мой комментарий ... извините
Билл Гейл
После установки 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"
Zskdan
источник
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"
mcaleaa
источник
3

Или заверните немного

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

Тогда это работает.

timerstart; sleep 2; timerstop
seconds=2
user3561136
источник
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 составляет 1 ч 20 м:

$ ./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])
18446744073709551615
источник
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
user5207022
источник
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 ~]# 
Sabrina
источник
-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

Не проверено, может быть глючит.

Ондра Жижка
источник