Come sommare il tempo usando bash?


12

Voglio sapere il tempo totale che una serie di processi impiegherebbe nel mio computer per decidere se dovrei correre lì o in un computer più forte. Quindi, sto prevedendo il tempo di esecuzione di ciascun comando. L'output è simile a:

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

Come posso sommare la seconda colonna per ottenere un tempo di esecuzione totale? Potrei provare a passare ogni riga

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

ma questo non ha senso in quanto i valori non sono numeri naturali.

Risposte:


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" +%sdà 0. Quindi date -u -d "jan 1 1970 00:03:34" +%sdà 214 secondi.


Sembra un po 'hack-y, ma ehi, funziona in un modo interessante (una specie di).
hjk,

4

Supponendo che tu stia usando il comando incorporato bash 'time', prima di eseguire il tuo programma, puoi farlo export TIMEFORMAT=%0R. L'output sarà quindi in interi secondi aggiungibili da awk. Maggiori informazioni sono disponibili nella sezione "Variabili della shell" della pagina man di bash.


4

Se non puoi (o non vuoi) usare il TIMEFORMATtuo solo bisogno di trasferire il tempo in secondi, quindi aggiungilo insieme. Ad esempio, l'output di pipe tramite:

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

Oppure, se lo desideri, puoi scambiare l'ultimo echocomando con

printf "%02d:%02d:%02d\n" $[sum/3600] $[sum/60] $[sum%60]

I minuti dovrebbero essere $[sum/60%60]per togliere le ore.
Jesse Chisholm,

3

Aggiornare:

Ecco una nuova implementazione che utilizza dcla "base di output". Se la somma totale è superiore a 60 ore, verranno generati quattro valori separati da spazi anziché tre. (E se la somma totale è inferiore a un'ora, verranno emessi solo due valori separati da spazio.)

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

Si presume che l'input sia in triple di ora, minuti, secondi, come mostrato nella domanda.

L'output sull'input fornito è:

 16 43

Risposta originale:

Facciamolo con il dctuo calcolatore da scrivania. È il back-end bc, ed è estremamente flessibile anche se spesso considerato criptico.


Innanzitutto, alcune pre-elaborazioni per fornire solo i tempi e convertire i due punti in spazi:

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

Potremmo farlo anche con Sed:

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

Andrò con Awk e trperché è più semplice. Uno dei comandi precedenti produce un output pulito nel seguente formato. (Sto usando il mio testo di esempio perché lo considero più interessante; ha ore incluse. Anche il tuo funzionerà.)

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

Dati i tempi nel formato sopra, eseguili attraverso il seguente script Sed e convoglia il risultato dccome mostrato:

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

(Suddiviso per ridurre lo scorrimento laterale :)

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

L'output sarà secondi, minuti, ore, in quella sequenza. (Nota che questa è una sequenza inversa.) Sto solo imparando, dcquindi questa non è una soluzione perfetta, ma penso che sia abbastanza buono per una prima occhiata dc.

Esempio di input e output, incollati direttamente dal mio terminale:

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

2

Ecco la mia soluzione: usare split(). Stampa tempo totale in secondi:

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

Stampa in un bel formato temporale:

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 supporta anche strftime, ma utilizzerà il tuo fuso orario corrente, quindi i risultati sarebbero confusi.


Puoi chiamare gawkcon TZ=UTC0 gawk...per rimuovere il problema del fuso orario.
Stéphane Chazelas,

2

Basta dividere e calcolare:

awk -F '[ :]' '
  {sum += $NF + 60 * ($(NF-1) + 60 * $(NF-2))}
  END {print sum}'

0

Variazione sui temi qui, ma più pipe

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

0

Quasi ogni shell potrebbe fare i conti:

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

Usa getseconds $=tin zsh per farlo dividere.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.