Your redirections do make FD 3 a copy of FD 1, so FD 3 will point to standard output and anything written to it will go to (by default) the TTY. You then redirect FD 1 to
/dev/null, so anything written to FD 1 will be discarded. Because
grep doesn't ever write to FD 3, nothing visible happens with it.
These happen in sequence: firstly a copy (
dup2) of FD 1 is made to FD 3, so FD 3 henceforth points at what FD 1 currently points at, and secondly FD 1 is replaced by a pointer to
The end result is shown in the following diagram:
Standard error (in pink, FD 2) and FD 3 are to the TTY, and standard output is to
/dev/null. FD 3 still points at the TTY because that's where FD 1 pointed when the copy was made. However, the
grep command will not try to write anything to FD 3, so the copy never gets any use.
The "copying" is directional: after
3>&1 is processed, anything written to FD 3 will go to where FD 1 pointed at the time of processing. It doesn't "preserve" anything beyond that: if you subsequently redirect FD 1, anything written to it goes to the new place. What you've done is held on to the original destination of FD 1 in case you wanted to use it later. The only redirection that affects where
grep's standard output ends up is the one
1>... that explicitly talks about where it goes.
grep were to write to FD 3, it would appear in the terminal as expected. Because it only outputs to FD 1 normally, all of its actual output is being discarded.
We could make a grep outputting to FD 3 if we wanted:
( grep "..." >&3 )
That will take the regular output on FD 1 and point it at (the newly-created) FD 3 instead. This won't work if you run it directly, because FD 3 doesn't go anywhere, but we can incorporate it into something like this that makes use of it:
( grep "..." >&3 ) 3>&1 1>/dev/null
The command in parentheses outputs to FD 3. The redirections afterwards 1) point FD 3 (which now actually has content on it) at FD 1, and 2) then direct FD 1 away again (which has no effect). The end result is that you'll get the
grep "..." output to standard output again, exactly where it would have been without all the fuss.
A practical use of this sort of redirection is something like
cmd 3>&1 1>&2 2>&3 3>&- | foo
which swaps FDs 1 and 2, using FD 3 as a temporary storage spot. It's also sometimes used for shell-script tricks, like faking process substitution in POSIX sh with
( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 ). Otherwise, it's fairly rare that commands innately use any non-standard file descriptor by number (of course many open a file themselves and get one).