How to measure time of program execution and store that inside a variable
To get the output of time
into a var use the following:
usr@srv $ mytime="$(time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$mytime"
real 0m0.006s
user 0m0.001s
sys 0m0.005s
You can also just ask for a single time type, e.g. utime:
usr@srv $ utime="$( TIMEFORMAT='%lU';time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$utime"
0m0.000s
To get the time you can also use date +%s.%N
, so take it before and after execution and calculate the diff:
START=$(date +%s.%N)
command
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
# echo $DIFF
In bash, the output of the time
construct goes to its standard error, and you can redirect the standard error of the pipeline it affects. So let's start with a command that writes to its output and error streamas: sh -c 'echo out; echo 1>&2 err'
. In order not to mix up the command's error stream with the output from time
, we can temporarily divert the command's error stream to a different file descriptor:
{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; }
This writes out
to fd 1, err
to fd 3, and the times to fd 2:
{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } \
3> >(sed 's/^/ERR:/') 2> >(sed 's/^/TIME:/') > >(sed 's/^/OUT:/')
It would be more pleasant to have err
on fd 2 and the times on fd 3, so we swap them, which is cumbersome because there's no direct way to swap two file descriptors:
{ { { time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } 3>&2 2>&4; } 4>&3; } 3> >(sed 's/^/TIME:/') 2> >(sed 's/^/ERR:/') > >(sed 's/^/OUT:/')
This shows how you can postprocess the output of the command, but if you want to capture both the output of the command and its times, you need to work harder. Using a temporary file is one solution. In fact, it's the only reliable solution if you need to capture both the command's standard error and its standard output. But otherwise, you can capture the whole output and take advantage of the fact that time
has a predictable format (if you use time -p
to get the POSIX format or the bash-specific TIMEFORMAT
variable).
nl=$'\n'
output=$(TIMEFORMAT='%R %U %S %P'; mycommand)
set ${output##*$nl}; real_time=$1 user_time=$2 system_time=$3 cpu_percent=$4
output=${output%$nl*}
If you only care about wall clock time, running date
before and after is a simple solution (if slightly more imprecise due to the extra time spent loading the external command).
If you are in bash
(and not sh
) and you DO NOT need sub-second accuracy, you can skip the call to date
entirely and do it without spawning any extra processes, without having to separate the combined output, and without having to capture and parse output from any commands:
# SECONDS is a bash special variable that returns the seconds since set.
SECONDS=0
mycmd <infile >outfile 2>errfile
DURATION_IN_SECONDS=$SECONDS
# Now `$DURATION_IN_SECONDS` is the number of seconds.