Como calcular o tempo decorrido no script bash?

224

Imprimo o horário de início e término usando date +"%T", o que resulta em algo como:

10:33:56
10:36:10

Como eu poderia calcular e imprimir a diferença entre esses dois?

Eu gostaria de obter algo como:

2m 14s
Misha Moroshko
fonte
5
Em outra nota, você não poderia usar o timecomando?
Anishsane 11/11
Use o tempo unix e date +%s, em seguida, subtraia para obter a diferença em segundos.
roblogic

Respostas:

476

O Bash possui uma SECONDSvariável interna útil que rastreia o número de segundos que passaram desde que o shell foi iniciado. Essa variável mantém suas propriedades quando atribuída a, e o valor retornado após a atribuição é o número de segundos desde a atribuição mais o valor atribuído.

Assim, você pode simplesmente definir SECONDScomo 0 antes de iniciar o evento cronometrado, simplesmente ler SECONDSapós o evento e fazer a aritmética da hora antes da exibição.

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

Como essa solução não depende date +%s(que é uma extensão GNU), é portátil para todos os sistemas suportados pelo Bash.

Daniel Kamil Kozar
fonte
59
+1. Aliás, você pode enganar datea execução da aritmética do tempo para você, escrevendo date -u -d @"$diff" +'%-Mm %-Ss'. (Isso interpreta $diffcomo segundos desde a época e calcula os minutos e segundos no UTC.) Isso provavelmente não é mais elegante, porém, apenas melhor ofuscado. :-P
ruakh 17/01
13
Agradável e simples. Se alguém precisar de horas:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
chus 19/05/2015
6
Deve ler $(date -u +%s)para evitar uma condição de corrida nas datas em que o horário de verão é alterado. E cuidado com os maus segundos de salto;)
Tino
3
@ daniel-kamil-kozar Bom achado. No entanto, isso é um pouco frágil. Existe apenas uma variável desse tipo, portanto ela não pode ser usada recursivamente para medir o desempenho e, pior ainda, depois que unset SECONDSela desaparecer:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino
6
@ParthianShot: Sinto muito que você pense assim. No entanto, acredito que esta resposta é o que a maioria das pessoas precisa enquanto procura respostas para uma pergunta como esta: medir quanto tempo passou entre os eventos, não calcular o tempo.
Daniel Kamil Kozar 23/02
99

Segundos

Para medir o tempo decorrido (em segundos), precisamos:

  • um número inteiro que representa a contagem de segundos decorridos e
  • uma maneira de converter esse número inteiro em um formato utilizável.

Um valor inteiro de segundos decorridos:

  • Existem duas maneiras internas do bash para encontrar um valor inteiro para o número de segundos decorridos:

    1. Variável de bash SECONDS (se SECONDS estiver desativado, ele perde sua propriedade especial).

      • Configurando o valor de SECONDS para 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • Armazenando o valor da variável SECONDSno início:

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

Converta esse número inteiro em um formato utilizável

O bash interno printfpode fazer isso diretamente:

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

similarmente

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

mas isso falhará por durações de mais de 24 horas, pois na verdade imprimimos um tempo de relógio de parede, não exatamente uma duração:

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

Para os amantes dos detalhes, em bash-hackers.org :

%(FORMAT)Tgera a string de data e hora resultante do uso de FORMAT como uma string de formato para strftime(3). O argumento associado é o número de segundos desde a época , ou -1 (hora atual) ou -2 (hora de inicialização do shell). Se nenhum argumento correspondente for fornecido, o horário atual será usado como padrão.

Portanto, convém ligar para textifyDuration $elpasedsecondsonde textifyDurationestá mais uma implementação da impressão por duração:

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

Data GNU.

Para obter um tempo formatado, devemos usar uma ferramenta externa (data GNU) de várias maneiras para atingir quase um ano e incluir nanossegundos.

Data interna da matemática.

Não há necessidade de aritmética externa, faça tudo em uma única etapa date:

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

Sim, existe um 0zero na cadeia de comando. É necessário.

Isso pressupõe que você possa alterar o date +"%T"comando para um date +"%s"comando para que os valores sejam armazenados (impressos) em segundos.

Observe que o comando está limitado a:

  • Valores positivos de $StartDatee $FinalDatesegundos.
  • O valor em $FinalDateé maior (mais tarde) que $StartDate.
  • Diferença horária menor que 24 horas.
  • Você aceita um formato de saída com horas, minutos e segundos. Muito fácil de mudar.
  • É aceitável usar -u horas UTC. Para evitar "DST" e correções de hora local.

Se você deve usar a 10:33:56sequência, basta convertê-la em segundos,
também, a palavra segundos pode ser abreviada como 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"

Observe que a conversão de segundos em tempo (como apresentada acima) é relativa ao início deste dia "hoje" (hoje).


O conceito pode ser estendido para nanossegundos, assim:

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"

Se for necessário calcular diferenças de tempo mais longas (até 364 dias), devemos usar o início de (alguns) anos como referência e o valor do formato %j(o número do dia no ano):

Igual a:

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

Infelizmente, nesse caso, precisamos subtrair manualmente 1UM do número de dias. O comando date exibe o primeiro dia do ano como 1. Não é tão difícil ...

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



O uso de um número longo de segundos é válido e documentado aqui:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


Data do Busybox

Uma ferramenta usada em dispositivos menores (um executável muito pequeno para instalar): Busybox.

Crie um link para o busybox chamado data:

$ ln -s /bin/busybox date

Use-o e chame-o date(coloque-o em um diretório incluído no PATH).

Ou crie um alias como:

$ alias date='busybox date'

A data do Busybox tem uma boa opção: -D para receber o formato do horário de entrada. Isso abre muitos formatos para serem usados ​​com o tempo. Usando a opção -D, podemos converter o tempo 10:33:56 diretamente:

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

E como você pode ver na saída do comando acima, presume-se que o dia seja "hoje". Para começar o tempo na época:

$ 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

A data do Busybox pode até receber a hora (no formato acima) sem -D:

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

E o formato de saída pode até demorar alguns segundos desde a época.

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

Nas duas vezes e um pouco de matemática bash (o busybox ainda não pode fazer as contas):

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

Ou formatado:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14
David Tonhofer
fonte
6
Esta é uma resposta tão surpreendente que cobriu muitas bases e com boas explicações. Obrigado!
Devy
Eu tive que procurar a %(FORMAT)Tsequência passada para o printfcomando integrado no bash. Em bash-hackers.org : imprima %(FORMAT)Ta string de data e hora resultante do uso FORMATcomo string de formato para strftime (3). O argumento associado é o número de segundos desde a Época, ou -1 (hora atual) ou -2 (hora de inicialização do shell). Se nenhum argumento correspondente for suprimento, o horário atual será usado como padrão . Isso é esotérico. Nesse caso, %scomo indicado strftime(3)é "o número de segundos desde a época".
David Tonhofer
35

Aqui está como eu fiz isso:

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

Realmente simples, calcule o número de segundos no início, depois o número de segundos no final e imprima a diferença em minutos: segundos.

Dorian
fonte
6
Como isso é diferente da minha solução? Eu realmente não vejo o benefício de chamar awkneste caso, já que o Bash lida com aritmética inteira igualmente bem.
precisa saber é o seguinte
1
Sua resposta também está correta. Algumas pessoas, como eu, preferem trabalhar com awk do que com inconsistências do bash.
Dorian
2
Você poderia elaborar um pouco mais sobre as inconsistências no Bash relacionadas à aritmética de números inteiros? Gostaria de saber mais sobre isso, já que não estava ciente de nada.
Daniel Kamil Kozar
12
Eu só estava procurando por isso. Não entendo as críticas a esta resposta. Eu gosto de ver mais de uma solução para um problema. E eu sou o que prefere awkcomandos para bash (se não for por mais nada, porque o awk funciona em outros shells). Gostei mais desta solução. Mas essa é a minha opinião pessoal.
rpsml
4
Zeros à esquerda: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($ 1/60), int ($ 1% 60)}'
Jon Strayer
17

Outra opção é usar a datediffpartir de 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

Você também pode usar gawk. mawk1.3.4 também tem strftimee mktime, mas versões mais antigas de mawke nawknão fazer.

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

Ou aqui está outra maneira de fazer isso com o 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
fonte
1
Eu estava procurando algo como o seu date método GNU . Gênio.
Haohmaru
Por favor, apague o meu comentário ... desculpe
Bill Gale
Após a instalação dateutilsvia apt, não havia datediffcomando - tinha que usar dateutils.ddiff.
Suzana
12

Eu gostaria de propor outra maneira que evite recuperar o datecomando. Pode ser útil se você já tiver coletado registros de %Tdata e hora no formato de data:

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"

podemos até lidar com milissegundos da mesma maneira.

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
fonte
1
Existe uma maneira de millisecons punho, por exemplo, 10: 33: 56,104
Umesh Rajbhandari
A manipulação de milissegundos se comporta mal se o campo de milissegundos começar com um zero. Por exemplo, ms = "033" fornecerá dígitos finais de "027" porque o campo ms está sendo interpretado como octal. alterar "eco" ... "+ ms))" para ... "+ $ {ms ## + (0)}))" corrigirá isso enquanto "shopt-s extglob" aparecer em algum lugar acima disso no roteiro. Deve-se, provavelmente, tira zeros à esquerda de h, m, e é bem ...
Eric Torres
3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))trabalhou para mim desde que chegueivalue too great for base (error token is "08")
Niklas
6

Aqui está um pouco de mágica:

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

sedsubstitui a :por uma fórmula para converter para 1/60. Em seguida, o cálculo do tempo feito porbc

redolente
fonte
5

A partir da data (GNU coreutils) 7.4, agora você pode usar -d para fazer aritmética:

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

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

As unidades que você pode usar são dias, anos, meses, horas, minutos e segundos:

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

Seguindo a resposta de Daniel Kamil Kozar, para mostrar horas / minutos / segundos:

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

Portanto, o script completo seria:

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

Ou embrulhe um pouco

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

Então isso funciona.

timerstart; sleep 2; timerstop
seconds=2
user3561136
fonte
3

Aqui está uma solução usando apenas os daterecursos de comandos usando "atrás" e não usando uma segunda variável para armazenar o tempo de término:

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

isto dá:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120
Zoltan K.
fonte
2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds
Joseph
fonte
6
Seria útil saber o que essa resposta faz que as outras respostas não fazem.
21413 Louis
Permite gerar facilmente a duração em qualquer formato permitido por date.
Ryan C. Thompson
2

date pode lhe dar a diferença e formatá-la para você (opções do OS X mostradas)

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

Algum processamento de cadeia pode remover esses valores vazios

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

Isso não funcionará se você colocar o horário anterior primeiro. Se você precisar lidar com isso, mude $(($(date ...) - $(date ...)))para$(echo $(date ...) - $(date ...) | bc | tr -d -)

Dave
fonte
1

Eu precisava de um script de diferença de horário para usar com mencoder( --endposé relativo), e minha solução é chamar um script Python:

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

frações de segundos também são suportadas:

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

e pode dizer que a diferença entre 200 e 120 é 1h 20m:

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

e pode converter qualquer número (provavelmente fracionário) de segundos ou minutos ou horas em 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])
18446744073709551615
fonte
1

Com o 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
fonte
1

Generalizando a solução do @ nisetama usando a data GNU (confiável 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

produzindo:

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

Defina esta função (digamos em ~ / .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
}

Agora você pode medir o tempo de partes de seus scripts:

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

$ ./script.sh
Total time: 0.5060

muito útil para encontrar gargalos de execução.

sergio
fonte
0

Sei que essa é uma postagem mais antiga, mas hoje a encontrei enquanto trabalhava em um script que usava datas e horas de um arquivo de log e calculava o delta. O script abaixo certamente é um exagero, e eu recomendo verificar minha lógica e matemática.

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

Você obterá a saída da seguinte maneira.

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.

Modifiquei um pouco o script para torná-lo independente (ou seja, basta definir valores variáveis), mas talvez a ideia geral também seja apresentada. Você pode querer uma verificação de erro adicional para valores negativos.

Daniel Blackmon
fonte
Caramba, muito trabalho !!. Por favor, olhe minha resposta: stackoverflow.com/a/24996647/2350426
0

Não posso comentar a resposta de mcaleaa, por isso posto aqui. A variável "diff" deve estar em minúsculas. Aqui está um exemplo.

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

A variável anterior foi declarada em minúscula. Foi o que aconteceu quando letras maiúsculas são usadas.

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

Então, a solução rápida seria assim

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

Aqui está minha implementação bash (com bits retirados de outros 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

Não testado, pode ser de buggy.

Ondra Žižka
fonte