Script Bash para calcular o tempo decorrido

118

Estou escrevendo um script em bash para calcular o tempo decorrido para a execução dos meus comandos, considere:

STARTTIME=$(date +%s)
#command block that takes time to complete...
#........
ENDTIME=$(date +%s)
echo "It takes $($ENDTIME - $STARTTIME) seconds to complete this task..."

Acho que minha lógica está correta, mas acabo com a seguinte impressão:

"Leva segundos para concluir esta tarefa ..."

Algo errado com a minha avaliação de cordas?

Eu acredito que as variáveis ​​do bash não têm tipo, eu adoraria se houvesse um método "string to integer" no bash.

Michael Mao
fonte

Respostas:

83

Quer $(())ou $[]vai funcionar para calcular o resultado de uma operação aritmética. Você está usando o $()que é simplesmente pegar a string e avaliá-la como um comando. É uma distinção um pouco sutil. Espero que isto ajude.

Como tink apontou nos comentários a esta resposta, $[]está obsoleto e $(())deve ser favorecido.

OmnipotentEntity
fonte
7
Você pode querer trocar os dois, já que a página do manual do bash 4.x afirma que $ [] está obsoleto e será removido em versões futuras.
toque em
2
Obrigado, eu não sabia.
OmnipotentEntity
157

Acho muito claro usar a variável interna "$ SECONDS"

SECONDS=0 ; sleep 10 ; echo $SECONDS

Lon Kaut
fonte
10
Somente sucesso =)
Lon Kaut
1
Precisa do sucesso, use o seu
Gromish
3
$SECONDSrealmente funciona para / bin / bash. Não funciona para / bin / dash, o shell padrão no Debian e Ubuntu.
Cameron Taggart
2
A desvantagem dessa solução é que ela mede apenas segundos inteiros, ou seja, não pode ser usada se você precisar de precisão inferior a um segundo.
Czechnology
@Czechnology sim, se você usar sleep 0.5em acima, o resultado às vezes é 0, às vezes 1 (pelo menos pelo Bash 5.0.3).
jarno
52

Você está tentando executar o número no ENDTIMEcomo um comando. Você também deve ver um erro como 1370306857: command not found. Em vez disso, use a expansão aritmética :

echo "It takes $(($ENDTIME - $STARTTIME)) seconds to complete this task..."

Você também pode salvar os comandos em um script separado, commands.sh , e usar o comando time:

time commands.sh
perreal
fonte
28

Você pode usar a timepalavra-chave do Bash aqui com uma string de formato apropriada

TIMEFORMAT='It takes %R seconds to complete this task...'
time {
    #command block that takes time to complete...
    #........
 }

Aqui está o que a referência diz sobreTIMEFORMAT :

O valor deste parâmetro é usado como uma string de formato especificando como as informações de tempo para pipelines prefixadas com a time palavra reservada devem ser exibidas. O %caractere ' ' introduz uma sequência de escape que é expandida para um valor de tempo ou outra informação. As sequências de escape e seus significados são os seguintes; as chaves denotam porções opcionais.

%%

    A literal ‘%’.
%[p][l]R

    The elapsed time in seconds.
%[p][l]U

    The number of CPU seconds spent in user mode.
%[p][l]S

    The number of CPU seconds spent in system mode.
%P

    The CPU percentage, computed as (%U + %S) / %R. 

O opcional p é um dígito que especifica a precisão, o número de dígitos fracionários após um ponto decimal. Um valor 0 faz com que nenhum ponto decimal ou fração seja gerado. No máximo três casas após o ponto decimal podem ser especificadas; valores de p maiores que 3 são alterados para 3. Se p não for especificado, o valor 3 será usado.

O opcional lespecifica um formato mais longo, incluindo minutos, no formato MMmSS.FFs. O valor de p determina se a fração é incluída ou não.

Se esta variável não for definida, o Bash age como se tivesse o valor

$'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'

Se o valor for nulo, nenhuma informação de tempo será exibida. Uma nova linha final é adicionada quando a string de formato é exibida.

gniourf_gniourf
fonte
10

Para números maiores, podemos imprimir em um formato mais legível. O exemplo abaixo faz o mesmo que outro, mas também imprime no formato "humano":

secs_to_human() {
    if [[ -z ${1} || ${1} -lt 60 ]] ;then
        min=0 ; secs="${1}"
    else
        time_mins=$(echo "scale=2; ${1}/60" | bc)
        min=$(echo ${time_mins} | cut -d'.' -f1)
        secs="0.$(echo ${time_mins} | cut -d'.' -f2)"
        secs=$(echo ${secs}*60|bc|awk '{print int($1+0.5)}')
    fi
    echo "Time Elapsed : ${min} minutes and ${secs} seconds."
}

Teste simples:

secs_to_human "300"
secs_to_human "305"
secs_to_human "59"
secs_to_human "60"
secs_to_human "660"
secs_to_human "3000"

Resultado:

Time Elapsed : 5 minutes and 0 seconds.
Time Elapsed : 5 minutes and 5 seconds.
Time Elapsed : 0 minutes and 59 seconds.
Time Elapsed : 1 minutes and 0 seconds.
Time Elapsed : 11 minutes and 0 seconds.
Time Elapsed : 50 minutes and 0 seconds.

Para usar em um script conforme descrito em outras postagens (capture o ponto inicial e chame a função com o tempo de término:

start=$(date +%s)
# << performs some task here >>
secs_to_human "$(($(date +%s) - ${start}))"
Mike Q
fonte
9

Experimente o seguinte código:

start=$(date +'%s') && sleep 5 && echo "It took $(($(date +'%s') - $start)) seconds"
Bulmaro Herrera
fonte
5

Esta é uma alternativa de uma linha para a função de Mike Q:

secs_to_human() {
    echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}
Erro do Servidor Interno
fonte
Agradável! Normalmente sou muito prolixo com meu código bash, isso é legal.
Mike Q
Combinar isso com SECONDSa resposta de Lon Kaut e ter em mente que $ / $ {} é desnecessário em variáveis ​​aritméticas torna o código tão curto que poderia até ser usado em linha:echo "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
ssc
2

tente usar o tempo com a opção de segundos decorridos:

/usr/bin/time -f%e sleep 1 sob bash.

ou \time -f%e sleep 1em bash interativo.

veja a página de manual do tempo:

Os usuários do shell bash precisam usar um caminho explícito para executar o comando de tempo externo e não a variante integrada do shell. No sistema onde o tempo está instalado em / usr / bin, o primeiro exemplo seria / usr / bin / time wc / etc / hosts

e

FORMATTING THE OUTPUT
...
    %      A literal '%'.
    e      Elapsed  real  (wall  clock) time used by the process, in
                 seconds.
Lynch
fonte
1
/bin/timenão vai funcionar aqui: OP menciona um bloco . Então, realmente precisamos da palavra-chave timeaqui.
gniourf_gniourf
-3
start=$(date +%Y%m%d%H%M%S);
for x in {1..5};
do echo $x;
sleep 1; done;
end=$(date +%Y%m%d%H%M%S);
elapsed=$(($end-$start));
ftime=$(for((i=1;i<=$((${#end}-${#elapsed}));i++));
        do echo -n "-";
        done;
        echo ${elapsed});
echo -e "Start  : ${start}\nStop   : ${end}\nElapsed: ${ftime}"

Start  : 20171108005304
Stop   : 20171108005310
Elapsed: -------------6
Rafał Białas
fonte
-3
    #!/bin/bash

    time_elapsed(){
    appstop=$1; appstart=$2

    ss_strt=${appstart:12:2} ;ss_stop=${appstop:12:2}
    mm_strt=${appstart:10:2} ;mm_stop=${appstop:10:2}
     hh_strt=${appstart:8:2} ; hh_stop=${appstop:8:2}
     dd_strt=${appstart:6:2} ; dd_stop=${appstop:6:2}
     mh_strt=${appstart:4:2} ; mh_stop=${appstop:4:2}
     yy_strt=${appstart:0:4} ; yy_stop=${appstop:0:4}

    if [ "${ss_stop}" -lt "${ss_strt}" ]; then ss_stop=$((ss_stop+60)); mm_stop=$((mm_stop-1)); fi
    if [ "${mm_stop}" -lt "0" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
    if [ "${mm_stop}" -lt "${mm_strt}" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
    if [ "${hh_stop}" -lt "0" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi
    if [ "${hh_stop}" -lt "${hh_strt}" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi

    if [ "${dd_stop}" -lt "0" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi
    if [ "${dd_stop}" -lt "${dd_strt}" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi

    if [ "${mh_stop}" -lt "0" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi
    if [ "${mh_stop}" -lt "${mh_strt}" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi

    ss_espd=$((10#${ss_stop}-10#${ss_strt})); if [ "${#ss_espd}" -le "1" ]; then ss_espd=$(for((i=1;i<=$((${#ss_stop}-${#ss_espd}));i++)); do echo -n "0"; done; echo ${ss_espd}); fi
    mm_espd=$((10#${mm_stop}-10#${mm_strt})); if [ "${#mm_espd}" -le "1" ]; then mm_espd=$(for((i=1;i<=$((${#mm_stop}-${#mm_espd}));i++)); do echo -n "0"; done; echo ${mm_espd}); fi
    hh_espd=$((10#${hh_stop}-10#${hh_strt})); if [ "${#hh_espd}" -le "1" ]; then hh_espd=$(for((i=1;i<=$((${#hh_stop}-${#hh_espd}));i++)); do echo -n "0"; done; echo ${hh_espd}); fi
    dd_espd=$((10#${dd_stop}-10#${dd_strt})); if [ "${#dd_espd}" -le "1" ]; then dd_espd=$(for((i=1;i<=$((${#dd_stop}-${#dd_espd}));i++)); do echo -n "0"; done; echo ${dd_espd}); fi
    mh_espd=$((10#${mh_stop}-10#${mh_strt})); if [ "${#mh_espd}" -le "1" ]; then mh_espd=$(for((i=1;i<=$((${#mh_stop}-${#mh_espd}));i++)); do echo -n "0"; done; echo ${mh_espd}); fi
    yy_espd=$((10#${yy_stop}-10#${yy_strt})); if [ "${#yy_espd}" -le "1" ]; then yy_espd=$(for((i=1;i<=$((${#yy_stop}-${#yy_espd}));i++)); do echo -n "0"; done; echo ${yy_espd}); fi

    echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}"
    #return $(echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}")
    }

    mh_days(){
    mh_stop=$1; yy_stop=$2; #also checks if it's leap year or not

    case $mh_stop in
     [1,3,5,7,8,10,12]) mh_stop=31
     ;;
     2) (( !(yy_stop % 4) && (yy_stop % 100 || !(yy_stop % 400) ) )) && mh_stop=29 || mh_stop=28
     ;;
     [4,6,9,11]) mh_stop=30
     ;;
    esac

    return ${mh_stop}
    }

    appstart=$(date +%Y%m%d%H%M%S); read -p "Wait some time, then press nay-key..." key; appstop=$(date +%Y%m%d%H%M%S); elapsed=$(time_elapsed $appstop $appstart); echo -e "Start...: ${appstart:0:4}-${appstart:4:2}-${appstart:6:2} ${appstart:8:2}:${appstart:10:2}:${appstart:12:2}\nStop....: ${appstop:0:4}-${appstop:4:2}-${appstop:6:2} ${appstop:8:2}:${appstop:10:2}:${appstop:12:2}\n$(printf '%0.1s' "="{1..30})\nElapsed.: ${elapsed}"

    exit 0


-------------------------------------------- return
Wait some time, then press nay-key...
Start...: 2017-11-09 03:22:17
Stop....: 2017-11-09 03:22:18
==============================
Elapsed.: 0000-00-00 00:00:01
Rafał Białas
fonte