Comment additionner le temps en utilisant bash?

12

Je veux connaître le temps total qu'une série de processus prendrait sur mon ordinateur pour décider si je devrais y courir ou dans un ordinateur plus puissant. Donc, je prévois le temps d'exécution de chaque commande. La sortie ressemble à:

process1    00:03:34
process2    00:00:35
process3    00:12:34

Comment additionner la deuxième colonne pour obtenir une durée totale de fonctionnement? Je pourrais essayer de parcourir chaque ligne

awk '{sum += $2 } END { print sum }

mais cela n'a aucun sens car les valeurs ne sont pas des nombres naturels.

je_b
la source

Réponses:

15
#!/bin/sh

EPOCH='jan 1 1970'
sum=0

for i in 00:03:34 00:00:35 00:12:34
do
  sum="$(date -u -d "$EPOCH $i" +%s) + $sum"
done
echo $sum|bc

date -u -d "jan 1 1970" +%sdonne 0. Donne donc date -u -d "jan 1 1970 00:03:34" +%s214 secondes.

Nizam Mohamed
la source
Cela semble un peu hack-y, mais bon, cela fonctionne d'une manière intéressante (en quelque sorte).
hjk
4

En supposant que vous utilisez la commande intégrée bash 'time', avant d'exécuter votre programme, vous le pouvez export TIMEFORMAT=%0R. La sortie sera ensuite ajoutée en secondes entières par awk. Plus d'informations sont disponibles dans la section 'Variables du shell' de la page de manuel bash.

Liczyrzepa
la source
4

Si vous ne pouvez pas (ou ne voulez pas) utiliser TIMEFORMATvotre simple besoin de transférer le temps en secondes, puis ajoutez-le ensemble. Par exemple, sortie de tuyau via:

{                                           
sum=0
while IFS="[ :]" proc_name h m s
do
    let sum+=$((60*($m+60*$h)+$s)) 
done 
echo $sum  
} 

Ou si vous le souhaitez, vous pouvez échanger la dernière echocommande par

printf "%02d:%02d:%02d\n" $[sum/3600] $[sum/60] $[sum%60]
Costas
la source
Les minutes devraient être $[sum/60%60]de supprimer les heures.
Jesse Chisholm
3

Mettre à jour:

Voici une nouvelle implémentation qui utilise dcla "base de sortie" de. Notez que si la somme totale est supérieure à 60 heures, cela produira quatre valeurs séparées par des espaces au lieu de trois. (Et si la somme totale est inférieure à une heure, seules deux valeurs séparées par des espaces seront générées.)

awk '{print $2}' file.txt | tr : \ | dc -f - -e '60o0ddd[+r60*+r60d**+z1<a]dsaxp'

L'entrée est supposée être en triplets d'heure, minute, seconde, comme indiqué dans la question.

La sortie sur l'entrée fournie est:

 16 43

Réponse originale:

Faisons cela avec dcvotre calculatrice de bureau. C'est le back-end bcet extrêmement flexible bien que souvent considéré comme cryptique.


Tout d'abord, un certain prétraitement pour ne donner que les heures, et convertir les deux-points en espaces:

awk '{print $2}' | tr : ' '

On pourrait aussi faire ça avec Sed:

sed -En -e 's/^.*([0-9][0-9]):([0-9][0-9]):([0-9][0-9]).*$/\1 \2 \3/p'

J'irai avec Awk et trparce que c'est plus simple. L'une des commandes ci-dessus produit une sortie propre au format suivant. (J'utilise mon propre exemple de texte parce que je le trouve plus intéressant; il comprend des heures. Le vôtre fonctionnera également.)

$ cat input
9 39 42
8 04 50
7 49 32
10 01 54
7 19 18

Étant donné les heures dans le format ci-dessus, exécutez-les via le script Sed suivant et canalisez le résultat dccomme indiqué:

sed -e '1s/^/0 /' -e 's/$/ r 60 * + r 60 60 * * + +/' -e '$s/$/ 60 60 * ~ 60 ~ f/' input | dc

(Décomposé pour réduire le défilement latéral :)

sed <input \
    -e '1s/^/0 /' \
    -e 's/$/ r 60 * + r 60 60 * * + +/' \
    -e '$s/$/ 60 60 * ~ 60 ~ f/' |
      dc 

La sortie sera en secondes, minutes, heures, dans cette séquence. (Notez que c'est une séquence inversée.) J'apprends juste dcdonc ce n'est pas une solution parfaite, mais je pense que c'est assez bon pour un premier regard dc.

Exemple d'entrée et de sortie, collé directement depuis mon terminal:

$ cat input 
9 39 42
8 04 50
7 49 32
10 01 54
7 19 18
$ sed -e '1s/^/0 /' -e 's/$/ r 60 * + r 60 60 * * + +/' -e '$s/$/ 60 60 * ~ 60 ~ f/' input | dc 
16
55
42
$ 
Caractère générique
la source
2

Voici ma solution - utiliser split(). Impression du temps total en secondes:

awk '{
        split($2, tm, ":");
        tottm += tm[3] + tm[2] * 60 + tm[1] * 3600;
    }
    END {
        print tottm;
    }' ptime

Impression au format de l'heure agréable:

awk '{
        split($2, tm, ":");
        secs += tm[3]; 
        mins += tm[2] + int(secs / 60); 
        hrs += tm[1] + int(mins / 60);
        secs %= 60; mins %= 60;
    }
    END {
        printf "%d:%d:%d\n", hrs, mins, secs;
    }' ptime

GNU awk prend également en charge strftime, mais il utilisera votre fuseau horaire actuel, donc les résultats seraient déroutants.

myaut
la source
Vous pouvez appeler gawkavec TZ=UTC0 gawk...pour supprimer le problème de fuseau horaire.
Stéphane Chazelas
2

Il suffit de diviser et de calculer:

awk -F '[ :]' '
  {sum += $NF + 60 * ($(NF-1) + 60 * $(NF-2))}
  END {print sum}'
Stéphane Chazelas
la source
0

Variation sur les thèmes ici, mais plus de tuyaux

echo """
process1    00:03:34
process2    00:00:35
process3    00:12:34
"""  | \
     sed 's/process.  *\([0-9]\)/\1/g' | \
     xargs -i{} date +%s --date "1970-1-1 {}" | \
     awk '{sum += $1; cnt +=1} 
         END {
               print sum / 60, " total minutes processing";
               print (sum / 3600) / cnt, "minutes per job"
         }'
916.717  total minutes processing
5.09287 minutes per job
Dr Bombay
la source
0

Presque n'importe quel shell pourrait faire le calcul:

#!/bin/sh

IFS=:; set +f
getseconds(){ echo "$(( ($1*60+$2)*60+$3 ))"; }

for   t in 00:03:34 00:00:35 00:12:34
do    sum=$((sum + $(getseconds $t) ))
done
printf '%s\n' "$sum"

Utilisez getseconds $=tdans zsh pour le diviser.

Isaac
la source