Parsing the output of Bash's time builtin

I'm running a C program from a Bash script, and running it through a command called time, which outputs some time statistics for the running of the algorithm.

If I were to perform the command

time $ALGORITHM $VALUE $FILENAME

It produces the output:

real    0m0.435s
user    0m0.430s
sys     0m0.003s

The values depending on the running of the algorithm

However, what I would like to be able to do is to take the 0.435 and assign it to a variable. I've read into awk a bit, enough to know that if I pipe the above command into awk, I should be able to grab the 0.435 and place it in a variable. But how do I do that?

Many thanks


Solution 1:

You must be careful: there's the Bash builtin time and there's the external command time, usually located in /usr/bin/time (type type -a time to have all the available times on your system).

If your shell is Bash, when you issue

time stuff

you're calling the builtin time. You can't directly catch the output of time without some minor trickery. This is because time doesn't want to interfere with possible redirections or pipes you'll perform, and that's a good thing.

To get time output on standard out, you need:

{ time stuff; } 2>&1

(grouping and redirection).

Now, about parsing the output: parsing the output of a command is usually a bad idea, especially when it's possible to do without. Fortunately, Bash's time command accepts a format string. From the manual:

TIMEFORMAT

The value of this parameter is used as a format string specifying how the timing information for pipelines prefixed with the time reserved word should be displayed. The % character introduces an escape sequence that is expanded to a time value or other information. The escape sequences and their meanings are as follows; the braces denote optional portions.

%%

   A literal `%`.

%[p][l]R

   The elapsed time in seconds.

%[p][l]U

   The number of CPU seconds spent in user mode.

%[p][l]S

   The number of CPU seconds spent in system mode.

%P

   The CPU percentage, computed as (%U + %S) / %R. 

The optional p is a digit specifying the precision, the number of fractional digits after a decimal point. A value of 0 causes no decimal point or fraction to be output. At most three places after the decimal point may be specified; values of p greater than 3 are changed to 3. If p is not specified, the value 3 is used.

The optional l specifies a longer format, including minutes, of the form MMmSS.FFs. The value of p determines whether or not the fraction is included.

If this variable is not set, Bash acts as if it had the value

$'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'

If the value is null, no timing information is displayed. A trailing newline is added when the format string is displayed.

So, to fully achieve what you want:

var=$(TIMEFORMAT='%R'; { time $ALGORITHM $VALUE $FILENAME; } 2>&1)

As @glennjackman points out, if your command sends any messages to standard output and standard error, you must take care of that too. For that, some extra plumbing is necessary:

exec 3>&1 4>&2
var=$(TIMEFORMAT='%R'; { time $ALGORITHM $VALUE $FILENAME 1>&3 2>&4; } 2>&1)
exec 3>&- 4>&-

Source: BashFAQ032 on the wonderful Greg's wiki.

Solution 2:

You could try the below awk command which uses split function to split the input based on digit m or last s.

$ foo=$(awk '/^real/{split($2,a,"[0-9]m|s$"); print a[2]}' file)
$ echo "$foo"
0.435

Solution 3:

You can use this awk:

var=$(awk '$1=="real"{gsub(/^[0-9]+[hms]|[hms]$/, "", $2); print $2}' file)
echo "$var"
0.435