Shell – Write output of `time` in a file, why are parentheses needed

io-redirectionshell-builtin

time writes to stderr, so one would assume that adding 2>&1 to the command line should route its output to stdout. But this does not work:

test@debian:~$ cat file 
one two three four
test@debian:~$ time wc file > wc.out 2>&1

real    0m0.022s
user    0m0.000s
sys     0m0.000s
test@debian:~$ cat wc.out 
 1  4 19 file

Only with parentheses it works:

test@debian:~$ (time wc file) > wc.out 2>&1
test@debian:~$ cat wc.out 
 1  4 19 file

real    0m0.005s
user    0m0.000s
sys     0m0.000s

Why are parentheses needed in this case? Why isn't time wc interpreted as one single command?

Best Answer

In ksh, bash and zsh, time is not a command (builtin or not), it's a reserved word in the language like for or while.

It's used to time a pipeline1.

In:

time for i in 1 2; do cmd1 "$i"; done | cmd2 > redir

You have special syntax that tells the shell to run that pipe line:

for i in 1 2; do cmd1 "$i"; done | cmd2 > redir

And report timing statistics for it.

In:

time cmd > output 2> error

It's the same, you're timing the cmd > output 2> error command, and the timing statistics still go on the shell's stderr.

You need:

{ time cmd > output 2> error; } 2> timing-output

Or:

exec 3>&2 2> timing-output
time cmd > output 2> error 3>&-
exec 2>&3 3>&-

For the shell's stderr to be redirected to timing-output before the time construct (again, not command) is used (here to time cmd > output 2> error 3>&-).

You can also run that time construct in a subshell that has its stderr redirected:

(time cmd > output 2> error) 2> timing-output

But that subshell is not necessary here, you only need stderr to be redirected at the time that time construct is invoked.

Most systems also have a time command. You can invoke that one by disabling the time keyword. All you need to do is quote that keyword somehow as keywords are only recognised as such when literal.

'time' cmd > output 2> error-and-timing-output

But beware the format may be different and the stderr of both time and cmd will be merged into error-and-timing-output.

Also, the time command, as opposed to the time construct cannot time pipelines or compound commands or functions or shell builtins...

If it were a builtin command, it might be able to time function invocations or builtins, but it could not time redirections or pipelines or compound commands.


1 Note that bash has (what can be considered as) a bug whereby time (cmd) 2> file (but not time cmd | (cmd2) 2> file for instance) redirects the timing output to file

Related Question