The key difference is very simply that the first pattern is fully portable, while the second one is a bash-specific shortcut for it. If you aren't using bash, you can't do the latter (unless some other shell has also adopted it that I don't know about).
File descriptors are certainly a hard thing to get your head around, and I'm still not all that proficient myself. But there are two confusing points, both related, that I finally understood that helped to clarify how it works.
1) The program sends its two kinds of output to file descriptors 1 and 2, and all the redirections you do for fd1 and fd2 are for these these two original outputs.
command 1>&2 2>file
...means that the command's stdout
is redirected to the shell's stderr output, and the command's stderr
is redirected to the file. It does NOT mean that you're sending fd1 into fd2, and then fd2 into the file, as if they were chained together. The second point is also involved here...
" does NOT
". It means "to the place fd2 is currently pointing". &
references the target
of the file descriptor, not the descriptor itself.
So taking these two:
command 2>&1 1>file
command 1>file 2>&1
The first one means, "send the command's stderr
to the current target of fd1, which is the screen, and the command's stdout
to the file".
The second line says "send the command's stdout
to the file, and send the command's stderr
to the current target of fd1, which has just been redefined as the file
". Therefore both outputs go into the file.
I hope that helps clear it up a bit.