Exibindo segundos como dias / horas / min / segundos?

31

É possível formatar facilmente segundos como um tempo legível por humanos no bash?

Não quero formatá-lo como uma data, mas como o número de dias / horas / minutos, etc ...

Tyilo
fonte
Você poderia fornecer um exemplo / vários exemplos, por favor?
Gabe.
1
Você está dizendo que tem um intervalo ou uma data desde a época?
Paul Tomblin

Respostas:

41

Você pode usar algo como isto:

function displaytime {
  local T=$1
  local D=$((T/60/60/24))
  local H=$((T/60/60%24))
  local M=$((T/60%60))
  local S=$((T%60))
  (( $D > 0 )) && printf '%d days ' $D
  (( $H > 0 )) && printf '%d hours ' $H
  (( $M > 0 )) && printf '%d minutes ' $M
  (( $D > 0 || $H > 0 || $M > 0 )) && printf 'and '
  printf '%d seconds\n' $S
}

Exemplos:

$ displaytime 11617
3 hours 13 minutes and 37 seconds
$ displaytime 42
42 seconds
$ displaytime 666
11 minutes and 6 seconds
Stéphane Gimenez
fonte
11

A maneira mais fácil e limpa de usar este liner (assumindo o GNU date):

Se o número de segundos for, diga:

seconds=123456789 # as in one of the answers above

eval "echo $(date -ud "@$seconds" +'$((%s/3600/24)) days %H hours %M minutes %S seconds')"

-> saída: 1428 days 21 hours 33 minutes 09 seconds

Jacques
fonte
Se a sua duração será sempre menor do que um dia você pode usar +%Tformato para Horas: Minutos: Segundos para este date +%T "1970-01-01 + ${seconds} seconds"para obter21:33:09
Ýzmir Ramirez
7

Os créditos são para Stéphane Gimenez, mas se alguém quiser exibir segundos apenas se um período for inferior a um minuto, aqui está minha versão modificada que eu uso (também com pluralização fixa):

converts()
{
    local t=$1

    local d=$((t/60/60/24))
    local h=$((t/60/60%24))
    local m=$((t/60%60))
    local s=$((t%60))

    if [[ $d > 0 ]]; then
            [[ $d = 1 ]] && echo -n "$d day " || echo -n "$d days "
    fi
    if [[ $h > 0 ]]; then
            [[ $h = 1 ]] && echo -n "$h hour " || echo -n "$h hours "
    fi
    if [[ $m > 0 ]]; then
            [[ $m = 1 ]] && echo -n "$m minute " || echo -n "$m minutes "
    fi
    if [[ $d = 0 && $h = 0 && $m = 0 ]]; then
            [[ $s = 1 ]] && echo -n "$s second" || echo -n "$s seconds"
    fi  
    echo
}

Um exemplo alternativo no POSIX:

converts(){
    t=$1

    d=$((t/60/60/24))
    h=$((t/60/60%24))
    m=$((t/60%60))
    s=$((t%60))

    if [ $d -gt 0 ]; then
            [ $d = 1 ] && printf "%d day " $d || printf "%d days " $d
    fi
    if [ $h -gt 0 ]; then
            [ $h = 1 ] && printf "%d hour " $h || printf "%d hours " $h
    fi
    if [ $m -gt 0 ]; then
            [ $m = 1 ] && printf "%d minute " $m || printf "%d minutes " $m
    fi
    if [ $d = 0 ] && [ $h = 0 ] && [ $m = 0 ]; then
            [ $s = 1 ] && printf "%d second" $s || printf "%d seconds" $s
    fi
    printf '\n'
}
dimir
fonte
Isso funcionou muito bem. Simplesmente colei a função no meu script e corri convert $spara obter uma saída bonita.
Flickerfly
3

Eu faria assim:

$ seconds=123456789; echo $((seconds/86400))" days "$(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds")
1428 days 21 hours 33 minutes 09 seconds
$

Aqui está o liner acima, dividido para facilitar a compreensão:

$ seconds=123456789
$ echo $((seconds/86400))" days"\
     $(date -d "1970-01-01 + $seconds seconds" "+%H hours %M minutes %S seconds")

No exemplo acima, estou repetindo a saída de outro comando que é executado dentro do $( ... )subcomando. Esse subcomando está fazendo isso, calculando o número de dias (segundos / 86400) e, em seguida, usando o datecomando em outro subcomando $(date -d ... ), para gerar horas, minutos e segundos para um determinado número de segundos.

atti
fonte
2

Modifiquei a função displaytime acima ... da seguinte maneira:

seconds2time ()
{
   T=$1
   D=$((T/60/60/24))
   H=$((T/60/60%24))
   M=$((T/60%60))
   S=$((T%60))

   if [[ ${D} != 0 ]]
   then
      printf '%d days %02d:%02d:%02d' $D $H $M $S
   else
      printf '%02d:%02d:%02d' $H $M $S
   fi
}

porque eu sempre quero ver HH: MM: SS, mesmo que sejam zeros.

Larry Helms
fonte
1

Estou construindo sobre a resposta de atti, que gostei como uma ideia.

Você pode fazer isso com o bash embutido, printfque levará alguns segundos desde a época como argumento. Não há necessidade de bifurcar-se para correr date.

Você precisa definir o fuso horário como UTC printfporque formata a hora no fuso horário local e você receberá a resposta errada se não estiver no horário UTC.

$ seconds=123456789
$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1428 days 21 hours 33 minutes 09 seconds

No meu horário local (que atualmente é NZDT - +1300), a resposta está errada se eu não definir o fuso horário

$ seconds=123456789
$ printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1428 days 09 hours 33 minutes 09 seconds

Com e sem definir o fuso horário

$ seconds=$(( 3600 * 25))
$ printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1 days 13 hours 00 minutes 00 seconds

$ TZ=UTC printf "%d days %(%H hours %M minutes %S seconds)T\n" $((seconds/86400)) $seconds
1 days 01 hours 00 minutes 00 seconds
Bill Ryder
fonte
Observe que o bash printfintroduziu essa %(datefmt)Tnotação começando com o bash-4.2-alpha.
bispo
-1

Aqui um

secs=378444
echo $(($secs/86400))d $(($(($secs - $secs/86400*86400))/3600))h:$(($(($secs - $secs/86400*86400))%3600/60))m:$(($(($secs - $secs/86400*86400))%60))s

Saída:

4d 9h:7m:24s
Shirish Shukla
fonte
-2

date --date '@1005454800'fornece Sun Nov 11 00:00:00 EST 20011005454800 segundos após a época do Unix. Você pode formatar isso com a opção data + FORMATO.

Paul Tomblin
fonte
7
Essa é uma data, não uma duração, sobre a qual a pergunta estava sendo feita.
Gilles 'SO- stop be evil'