First, with some exceptions, redirections generally occur at the point they are written. They are removed from the command-and-argument sequence; only the remaining, non-redirection, words participate. Thus:
3>&1 foo 1>&2 arg1 2>&3 arg2 3>&- arg3
runs foo arg1 arg2 arg3 with stdout and stderr swapped, because:
3>&1 makes a copy (more precisely, a dup2) of fd 1 (stdout) on fd 3
1>&2 makes a copy of fd 2 (stderr) on fd 1 (so now stdout and stderr both go wherever they were going)
2>&3 makes a copy of fd 3 (saved original stdout) on fd 2 (stderr)
3>&- closes fd 3.
(The notable exception is that piped output "happens first", even though the pipe symbol is at the end of the simple-command part.)
Second, as pje noted, time is a bash built-in. time foo runs the command, then prints the time summary to bash's stderr. The time keyword is effectively removed first, then the remaining command sequence is handled as usual. (This applies even if the command is a pipeline: time times the entire pipeline.)
In this case, the command sequence is one simple command, with redirections:
2>&1 sh 2>&1 -c ... > file
Each redirection happens at the point it is written, and the remaining sequence is:
sh -c ...
The redirections are:
- send stderr to wherever stdout is currently going
- send stderr to stdout, again (this has no new effect)
- send stdout to
file.
so sh -c is run with its stderr going to your stdout, and its stdout going to file file. As you note, dd prints its output to (its) stderr, which is sh -c ...'s stderr, which is your stdout.
If you run:
time 2>file dd if=/dev/zero of=ddfile bs=512 count=125
you will get time's stderr output on your stderr (e.g., screen), and dd's stderr on file file. (This happens no matter how far right you slide the 2>file part, as long as it remains part of the simple-command.)
If you try:
2>file time ...
you will find time's output redirected, but this defeats the built-in time entirely, running /usr/bin/time instead. To get bash's built-in to fire, time has to be up front. You can make a sub-shell:
(time ...) 2>file
or a sub-block as pje illustrated:
{ time ...; } 2>file
(the syntax is clumsier with the sub-block as white space and/or semicolons are needed, but it avoids a fork system call).