Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

In order to find out how long certain operations within a Bash (v4+) script take, I would like to parse the output from the time command "separately" and (ultimately) capture it within a Bash variable (let VARNAME=...).

Now, I am using time -f '%e' ... (or rather command time -f '%e' ... because of the Bash built-in), but since I already redirect the output of the executed command I'm really lost as to how I would go about to capture the output of the time command. Basically the problem here is to separate the output of time from the output of the executed command(s).

What I want is the functionality of counting the amount of time in seconds (integers) between starting a command and its completion. It doesn't have to be the time command or the respective built-in.

Edit: given the two useful answers below, I wanted to add two clarifications.

  1. I do not want to throw away the output of the executed command, but it will not really matter whether it ends up on stdout or stderr.
  2. I would prefer a direct approach over an indirect one (i.e. catching output directly as opposed to store it in intermediate files).

The solution using date so far comes closes to what I want.

share|improve this question
The most direct way to get the data and handle it while still letting it run normally would be to do it in a C program using fork(), execvp() and wait3()/wait4(). This is ultimately what time and friends are doing. I'm not aware of a simle way to do it in bash/perl without redirecting to a file or similar approach. – penguin359 Apr 26 '11 at 22:20
There is a related question that you might find interesting going on here. – Caleb Apr 26 '11 at 23:09
@Caleb: thanks. Indeed interesting. However, for my purposes it's sufficient to do it in a script. – 0xC0000022L Apr 26 '11 at 23:28
up vote 44 down vote accepted

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"

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)
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
# echo $DIFF

Hope that helps.

share|improve this answer
I didn't want to throw away the output from the command though. So I guess your third code block is closest to what I had in mind. Although I'd write the last one as DIFF=$((END-START)), making use of the arithmetic expressions. :) ... thanks for the answer. +1 – 0xC0000022L Apr 26 '11 at 20:00
@STATUS_ACCESS_DENIED: Bash doesn't do floating point arithmetic, so if you want better than second resolution (.%N in binfalse's code), you either need bc or fancier calculations. – Gilles Apr 26 '11 at 21:11
@Gilles: I know. As I wrote in my question integers are fine. No need to have a higher resolution than second. But thanks, the date invocation would have to be changed. I had realized that, though. – 0xC0000022L Apr 26 '11 at 23:25
FYI, the date formatting %N doesn't seem to work on Mac OS X, it just returns "N". Ok on Ubuntu. – David Oct 19 '15 at 21:24
Note that while the time (cmd) 2> something works at redirecting the timing output to file, it's not meant to (as per documentation), doesn't in other shells where time is a keyword and could be considered as a bug. I wouldn't rely on it as it may not work in future versions of bash. – Stéphane Chazelas Jan 29 at 10:33

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

output=$(TIMEFORMAT='%R %U %S %P'; mycommand)
set ${output##*$nl}; real_time=$1 user_time=$2 system_time=$3 cpu_percent=$4

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

share|improve this answer
Wow, a lot to test. Thanks a lot for the extensive reply. +1. – 0xC0000022L Apr 26 '11 at 23:25

With time, the command output comes out on stdout and the time comes out on stderr. So, to separate them, you can do:

command time -f '%e' [command] 1>[command output file] 2>[time output file]

But, now the time is in a file. I don't think Bash is able to put stderr in a variable directly. If you don't mind redirecting the command's output somewhere, you can do:

FOO=$((( command time -f '%e' [command]; ) 1>outputfile; ) 2>&1; )

When you do this, the command's output will be in outputfile and the time it took to run will be in $FOO.

share|improve this answer
ooh, I didn't realize. I knew time writes to stderr, but I didn't realize that it would merge stdout and stderr of the executed command into its stdout. But generally there is no direct method (i.e. without intermediate file)? +1. – 0xC0000022L Apr 26 '11 at 19:57
@STATUS_ACCESS_DENIED: time does not merge its command's stdout and stderr. The way I showed assumed that you only needed the command's stdout output. Since bash will only store what comes out of stdout, you have to redirect. In case you can safely drop your command's stderr: the time will always be on the last line of stderr. If you do need both your command's output streams, I'd suggest wrapping it in another script. – marinus Apr 26 '11 at 20:27

For that purpose it's probably better to use times (than time) in bash which prints the accumulated user and system times for the shell and for processes run from the shell, example use:

$ (sleep 2; times) | (read tuser tsys; echo $tuser:$tsys)

See: help -m times for more info.

share|improve this answer

try this, it runs a simple command with arguments and puts the times $real $user $sys and preserves the exit code.

It also does not fork subshells or trample on any variables except real user sys, and does not otherwise interfere with the running of the script

timer () {
  { time { "$@" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  set -- $?
  read -d "" _ real _ user _ sys _ < "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  rm -f "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  return $1


  timer find /bin /sbin /usr rm /tmp/
  echo $real $user $sys

note: it only times the simple command not a pipeline, whose components are run in a subshell

This version allows you to specify as $1 the name of the variables that should receive the 3 times:

timer () {
  { time { "${@:4}" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  set -- $? "$@"
  read -d "" _ "$2" _ "$3" _ "$4" _ < "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  rm -f "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  return $1


  timer r u s find /bin /sbin /usr rm /tmp/
  echo $r $u $s

and may be useful if it ends up being called recursively, to avoid trampling on times; but then r u s etc should be declared local in their use.


share|improve this answer
date; ls -la; date
date; vagrant up; date
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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