10

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.

12
#!/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.

  • Does seem a little hack-y, but hey, it works in an interesting (sort-of) way. – h.j.k. Mar 6 '15 at 5:41
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.

4

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]
2

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.

  • You can call gawk with TZ=UTC0 gawk... to remove the timezone issue. – Stéphane Chazelas Dec 3 '16 at 7:23
2

Just split and calculate:

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.