Jak zsumować czas za pomocą bash?

12

Chcę wiedzieć, ile czasu zajmie seria procesów na moim komputerze, aby zdecydować, czy mam tam działać, czy na silniejszym komputerze. Więc prognozuję czas działania każdego polecenia. Dane wyjściowe wyglądają następująco:

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

Jak mogę zsumować drugą kolumnę, aby uzyskać całkowity czas działania? Mógłbym spróbować przepuścić każdą linię

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

ale to nie ma sensu, ponieważ wartości nie są liczbami naturalnymi.

je_b
źródło

Odpowiedzi:

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" +%sdaje 0. Więc date -u -d "jan 1 1970 00:03:34" +%sdaje 214 sek.

Nizam Mohamed
źródło
Wydaje się trochę hack-y, ale hej, działa w interesujący (niejako) sposób.
hjk
4

Zakładając, że używasz wbudowanego polecenia bash „time”, zanim uruchomisz program, możesz export TIMEFORMAT=%0R. Dane wyjściowe będą następnie dodawane przez awk przez całe sekundy. Więcej informacji jest dostępnych w sekcji „Zmienne powłoki” na stronie podręcznika bash.

Liczyrzepa
źródło
4

Jeśli nie możesz (lub nie chcesz) użyć TIMEFORMATswojego czasu, wystarczy przeliczyć czas na sekundy, a następnie dodać go razem. Na przykład wyjście rury przez:

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

Lub jeśli chcesz, możesz wymienić ostatnie echopolecenie przez

printf "%02d:%02d:%02d\n" $[sum/3600] $[sum/60] $[sum%60]
Costas
źródło
Minuty powinny być $[sum/60%60]pozbawione godzin.
Jesse Chisholm
3

Aktualizacja:

Oto nowa implementacja, która wykorzystuje dc„bazę wyjściową”. Zauważ, że jeśli łączna suma jest większa niż 60 godzin, spowoduje to wygenerowanie czterech wartości oddzielonych spacjami zamiast trzech. (A jeśli całkowita suma jest mniejsza niż jedna godzina, zostaną wyświetlone tylko dwie wartości rozdzielone spacjami.)

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

Zakłada się, że dane wejściowe będą trzykrotnie godziny, minuty, sekundy, jak pokazano w pytaniu.

Dane wyjściowe na dostarczonym wejściu to:

 16 43

Oryginalna odpowiedź:

Zróbmy to za pomocą dckalkulatora biurkowego. Jest to back-end bci jest niezwykle elastyczny, choć często uważany za tajemniczy.


Po pierwsze, niektóre wstępne przetwarzanie, aby podać tylko czasy i przekonwertować dwukropki na spacje:

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

Możemy to również zrobić za pomocą Sed:

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

Pójdę z Awk i trponieważ jest to prostsze. Każde z powyższych poleceń generuje czysty wynik w następującym formacie. (Używam własnego przykładowego tekstu, ponieważ uważam go za bardziej interesujący; zawiera godziny. Twój też będzie działał).

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

Biorąc pod uwagę czasy w powyższym formacie, uruchom je za pomocą następującego skryptu Sed i potokuj wynik, dcjak pokazano:

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

(Podzielone, aby zmniejszyć przewijanie w bok :)

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

Dane wyjściowe będą w sekundach, minutach, godzinach w tej sekwencji. (Zauważ, że jest to odwrócona sekwencja.) Właśnie się uczę, dcwięc nie jest to idealne rozwiązanie, ale myślę, że jest całkiem dobry na pierwsze spojrzenie dc.

Przykładowe dane wejściowe i wyjściowe, wklejone bezpośrednio z mojego terminala:

$ 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
$ 
Dzika karta
źródło
2

Oto moje rozwiązanie - użyj split(). Drukowanie całkowitego czasu w sekundach:

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

Drukowanie w przyjemnym formacie czasowym:

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 obsługuje również strftime, ale użyje twojej bieżącej strefy czasowej, więc wyniki byłyby mylące.

myaut
źródło
Możesz zadzwonić gawkz, TZ=UTC0 gawk...aby usunąć problem strefy czasowej.
Stéphane Chazelas,
2

Po prostu podziel i oblicz:

awk -F '[ :]' '
  {sum += $NF + 60 * ($(NF-1) + 60 * $(NF-2))}
  END {print sum}'
Stéphane Chazelas
źródło
0

Odmiana tematów tutaj, ale więcej fajek

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
źródło
0

Prawie każda skorupa mogłaby wykonać matematykę:

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

Użyj getseconds $=tw zsh, aby podzielić.

Izaak
źródło