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