18

I just want to capture the output of a time command i.e:

X=$(time ls)

or

$(time ls) | grep real

The time function spits it to the console though. How do I do this?

5 Answers 5

18

X=`(time ls) 2>&1 | grep real`

1
  • You don't need a subshell just for time; Dennis Williamson's answer is better in that regard.
    – musiphil
    Nov 1, 2013 at 21:32
16

See BashFAQ/032.

$ # captures output of command and time
$ time=$( TIMEFORMAT="%R"; { time ls; } 2>&1 )    # note the curly braces

$ # captures the time only, passes stdout through
$ exec 3>&1 4>&2
$ time=$(TIMEFORMAT="%R"; { time ls 1>&3 2>&4; } 2>&1)
bar baz
$ exec 3>&- 4>&-

The time will look like "0.000" using TIMEFORMAT="%R" which will be the "real" time.

4
  • could you explain a little how this works ? I've uses a snippet like this cargo cult style for years. Whats stream 3 and 4 ? and the '-' ?
    – Sirex
    Sep 15, 2016 at 2:46
  • 2
    @Sirex: Streams 3 and 4 are streams created by the exec command in my answer using available file descriptors. Any available file descriptors could be used. Streams 3 and 4 are copies of 1 (stdout) and 2 (stderr), respectively. This allows the output of ls to pass normally to stdout and stderr via 3 and 4 while the output of time (which normally goes to stderr) is redirected to the original stdout (1) and then captured in the variable using command substitution. As you can see in my example, the filenames bar and baz are output to the terminal. ... Sep 15, 2016 at 15:48
  • 1
    ... Once finished, the extra streams are closed using the trailing -. Sep 15, 2016 at 15:49
  • Running this with tracing mode (set -x) adds some unwanted output to the result. I solved it by adding set +x so that scripts won't fail while debugging. So the solution that worked for me: time=$(set +x; TIMEFORMAT="%R"; { time ls 1>&3 2>&4; } 2>&1) May 3, 2023 at 14:16
5

Time writes its output to STDERR rather than STDOUT. Making matters worse, by default 'time' is a shell builtin command, so if you attempt 'time ls 2>&1' the '2>&1' only applies to 'ls'.

The solution would probably be something like:

/usr/bin/time -f 'real %e' -o OUTPUT_FILE ls > /dev/null 2>&1<br>
REALTIME=$(cat OUTPUT_FILE | cut -f 2 -d ' ')

There are more fancy ways to do it, but that is the clear/simple way.

1
  • I am interested in doing this without -o. Can you post one of the fancy methods you were thinking of :) ? Apr 22, 2014 at 17:12
3

The cleanest approach is to combine a temp file and use GNU's time.

command="sleep 1"
saved_timing=`mktemp`
/usr/bin/time --format="%E real" -o "$saved_timing" $command
elapsed=$(cat "$saved_timing")
echo $elapsed

This will not bypass normal command output and you don't have to care about the file either since it will be deleted on it own.

2

@Dennis Williamson's answer is great, but it doesn't help you store the command's output in one variable, and the output of time in another variable. This is actually not possible using file descriptors.

If you want to record how long a program takes to run, you could do this by just subtracting the start time from the end time. Here is a simple example that shows how many milliseconds a program took to run:

START_TIME=$(date +%s%3N)
OUTPUT=$(ls -l)
ELAPSED_TIME=$(expr $(date +%s%3N) - $START_TIME)
echo "Command finished in $ELAPSED_TIME milliseconds"

This is not quite as accurate as the time command, but it should work perfectly fine for most bash scripts.

Unfortunately Mac's date command doesn't support the %N format, but you can install coreutils (brew install coreutils) and use gdate:

START_TIME=$(gdate +%s%3N)
OUTPUT=$(ls -l)
ELAPSED_TIME=$(expr $(gdate +%s%3N) - $START_TIME)
echo "Command finished in $ELAPSED_TIME milliseconds"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .