13

I want to know the total amount of time that a series of processes would take in my computer to decide if I should running there or in a stronger computer. So, i am forecasting the running time of each command. The output looks like:

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

How can I sum the second column to obtain a total running time? I could try pipping each line through

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

but this makes no sense as the values are not natural numbers.

9 Answers 9

18
#!/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" +%s gives 0. So date -u -d "jan 1 1970 00:03:34" +%s gives 214 secs.

1
  • Does seem a little hack-y, but hey, it works in an interesting (sort-of) way.
    – h.j.k.
    Commented Mar 6, 2015 at 5:41
5

If you cannot (or don't want) use TIMEFORMAT your just need to transfer time into seconds, then add it together. For example pipe output through:

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

Or if you'd like can exchange last echo-command by

printf "%02d:%02d:%02d\n" $[sum/3600] $[sum/60] $[sum%60]
1
  • The minutes should be $[sum/60%60] to strip the hours off. Commented Feb 27, 2020 at 16:35
5

Update:

Here is a new implementation that makes use of dc's "output base." Note that if the total sum is more than 60 hours, this will output four space-separated values instead of three. (And if the total sum is less than one hour, only two space-separated values will be output.)

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

Input is assumed to be in triples of hour, minute, second, as shown in the question.

Output on the provided input is:

 16 43

Original answer:

Let's do this with dc your Desk Calculator. It's the back-end to bc, and it's extremely flexible although often considered cryptic.


First, some pre-processing to give the times only, and convert the colons to spaces:

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

We could also do this with Sed:

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

I'll go with Awk and tr because it's simpler. Either of the above command produces a clean output in the following format. (I'm using my own example text because I consider it more interesting; it has hours included. Yours will work as well.)

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

Given times in the above format, run them through the following Sed script and pipe the result into dc as shown:

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

(Broken down to reduce sideways scrolling:)

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

The output will be seconds, minutes, hours, in that sequence. (Note this is a reversed sequence.) I'm just learning dc so this isn't a perfect solution, but I think it's pretty good for a first look at dc.

Example input and output, pasted directly from my 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
$ 
4

Assuming you are using the bash 'time' builtin command, before you run your program, you can export TIMEFORMAT=%0R. The output will then be in whole seconds addable by awk. More information is available in the 'Shell Variables' section of the bash man page.

3

Here is my solution - use split(). Printing total time in seconds:

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

Printing in nice time format:

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 also supports strftime, but it will use your current timezone, so results would be confusing.

1
  • 1
    You can call gawk with TZ=UTC0 gawk... to remove the timezone issue. Commented Dec 3, 2016 at 7:23
2

Just split and calculate:

awk -F '[ :]' '
  {sum += $NF + 60 * ($(NF-1) + 60 * $(NF-2))}
  END {print sum}'
1
echo 'process1    00:03:34
process2    00:00:35
process3    00:12:34'|awk '{print $2}'|xargs -I {} gdate -d "1970-01-01 {} UTC" +%s|jq -sr 'add|(.|strftime("%H:%M:%S"))'
00:16:43

extract the elements from the second column

awk '{print $2}'

pass each line's timestamp into gdate command to convert it to a UTC timestamp

xargs -I {} gdate -d "1970-01-01 {} UTC" +%s

Convert it to a numerical array using jq, then sum them up to get the total number of seconds, and finally convert it to the "%H:%M:%S" format using strftime:

jq -sr 'add|(.|strftime("%H:%M:%S"))'
2
  • This seems like a bit of a Rube Goldberg machine built around an uncredited rip-off of Nizam Mohamed’s answer.  You should at least explain it. Commented Apr 20 at 21:22
  • 1
    @g-man-says-reinstate-monica No, I came up with it myself.I appended explanations for the commands Commented Apr 21 at 4:36
0

Variation on themes here, but more pipes

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

Almost any shell could do the math:

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

Use getseconds $=t in zsh to get it to split.

You must log in to answer this question.