Script bash pour calculer le temps écoulé

118

J'écris un script en bash pour calculer le temps écoulé pour l'exécution de mes commandes, considérez:

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

Je suppose que ma logique est correcte, mais je me retrouve avec l'impression suivante:

"Cela prend quelques secondes pour terminer cette tâche ..."

Quelque chose ne va pas avec mon évaluation de chaîne?

Je crois que les variables bash ne sont pas typées, j'aimerais quand même s'il existe une méthode "string to integer" dans bash.

Michael Mao
la source

Réponses:

83

Soit $(())ou $[]fonctionnera pour calculer le résultat d'une opération arithmétique. Vous utilisez $()ce qui consiste simplement à prendre la chaîne et à l'évaluer comme une commande. C'est un peu une distinction subtile. J'espère que cela t'aides.

Comme tink l'a souligné dans les commentaires sur cette réponse, $[]est obsolète et $(())devrait être favorisée.

Entité omnipotente
la source
7
Vous voudrez peut-être échanger ces deux éléments, car la page de manuel bash 4.x indique que $ [] est obsolète et sera supprimé dans les versions futures.
tink le
2
Merci, je n'étais pas au courant.
OmnipotentEntity
157

Je trouve très propre d'utiliser la variable interne "$ SECONDS"

SECONDS=0 ; sleep 10 ; echo $SECONDS

Lon Kaut
la source
10
Seul succès =)
Lon Kaut
1
Besoin de succès, utilisez le vôtre
Gromish
3
$SECONDSfonctionne effectivement pour / bin / bash. Cela ne fonctionne pas pour le / bin / dash, le shell par défaut dans Debian et Ubuntu.
Cameron Taggart
2
L'inconvénient de cette solution est qu'elle ne mesure que des secondes entières, c'est-à-dire qu'elle n'est pas utilisable si vous avez besoin d'une précision inférieure à la seconde.
Czechnology
@Czechnology oui, si vous utilisez sleep 0.5ci-dessus, le résultat est parfois 0, parfois 1 (au moins par Bash 5.0.3).
jarno
52

Vous essayez d'exécuter le numéro dans le ENDTIMEsous forme de commande. Vous devriez également voir une erreur comme 1370306857: command not found. Utilisez plutôt le développement arithmétique :

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

Vous pouvez également enregistrer les commandes dans un script séparé commands.shet utiliser la commande time:

time commands.sh
perréel
la source
28

Vous pouvez utiliser le timemot - clé de Bash ici avec une chaîne de format appropriée

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

Voici ce que ditTIMEFORMAT la référence :

La valeur de ce paramètre est utilisée comme chaîne de format spécifiant la manière dont les informations de synchronisation pour les pipelines préfixés par le time mot réservé doivent être affichées. Le caractère « %» introduit une séquence d'échappement qui est étendue à une valeur de temps ou à d'autres informations. Les séquences d'échappement et leurs significations sont les suivantes; les accolades désignent des parties facultatives.

%%

    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. 

Le p optionnel est un chiffre spécifiant la précision, le nombre de chiffres fractionnaires après une virgule décimale. Une valeur de 0 ne produit aucun point décimal ni aucune fraction. Au plus trois places après la virgule décimale peuvent être spécifiées; les valeurs de p supérieures à 3 sont remplacées par 3. Si p n'est pas spécifié, la valeur 3 est utilisée.

L'option facultative lspécifie un format plus long, y compris les minutes, de la forme MMmSS.FFs. La valeur de p détermine si la fraction est incluse ou non.

Si cette variable n'est pas définie, Bash agit comme si elle avait la valeur

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

Si la valeur est nulle, aucune information de minutage n'est affichée. Une nouvelle ligne de fin est ajoutée lorsque la chaîne de format est affichée.

gniourf_gniourf
la source
10

Pour des nombres plus grands, nous souhaitons peut-être imprimer dans un format plus lisible. L'exemple ci-dessous fait la même chose que les autres mais s'imprime également au format "humain":

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

Test simple:

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

Production:

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.

A utiliser dans un script comme décrit dans d'autres articles (capturez le point de départ puis appelez la fonction avec l'heure de fin:

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

Essayez le code suivant:

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

Ceci est une alternative à la fonction de Mike Q:

secs_to_human() {
    echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}
Erreur Interne du Serveur
la source
Agréable! Je suis généralement très bavard avec mon code bash, c'est cool.
Mike Q
Combiner cela avec SECONDSla réponse de Lon Kaut et garder à l'esprit que $ / $ {} n'est pas nécessaire sur les variables arithmétiques rend le code si court qu'il pourrait même être utilisé en ligne:echo "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
ssc
2

essayez d'utiliser le temps avec l'option secondes écoulées:

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

ou \time -f%e sleep 1en bash interactif.

voir la page de manuel time:

Les utilisateurs du shell bash doivent utiliser un chemin explicite pour exécuter la commande de temps externe et non la variante intégrée du shell. Sur un système où l'heure est installée dans / usr / bin, le premier exemple deviendrait / usr / bin / time wc / etc / hosts

et

FORMATTING THE OUTPUT
...
    %      A literal '%'.
    e      Elapsed  real  (wall  clock) time used by the process, in
                 seconds.
Lyncher
la source
1
/bin/timene fonctionnera pas ici: OP mentionne un bloc . Nous avons donc vraiment besoin du mot-clé timeici.
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
la source
-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
la source