Ok ... I am assuming you are having similar trouble understanding the file descriptors as I did. Let us have a look at the following
{ configure_commands 3>&1 1>&2 2>&3 | tee "$HOME/configure.err" ;} &>"$HOME/configure.log"
When I first saw that I had some trouble understanding why this catches stderr in a separate log. stderr and stdout are being switched so that error messages are being sent to stdout and "normal" messages go to stderr. That is what enables us to catch error messages in a dedicated logfile, since a pipe connects the first programs stdout with stdin of the second command. The redirections outside the {} then catches all output in a single file.
Furthermore confusing was the fact, that the redirections are "order" sensitive. The above statement is NOT equivalent to
{ configure_commands 3>&1 2>&3 1>&2 | tee "$HOME/configure.err" ;} &>"$HOME/configure.log"
This will generate two logfiles which catch the output of both stderr and stdout. I found the following way of looking at it as helpful to understand what happens:
Do
not think of fd1 and stdout as one and the same thing. Think of stdout of some immutable object that
cannot be redirected. fd1 is a variable (or pointer) that points to stdout. Think of fd2 accordingly. So you have something like this
Code:
pointer to Object
fd1 ---> STDOUT
fd2 ---> STDERR
STDOUT and STDERR are only accessible for us by using the filedescriptors fd1 and fd2. Now let us see what happens when you do something like
2>&1
You are pointing the "variable" fd2 to the same address that fd1 points to. Notice the similarity of the addressOf operator & in c/c++. Now what does our "reference table" look like?
Code:
pointer to Object
fd1 ---> STDOUT
fd2 ---> STDOUT
??? ---> STDERR
As you see, there is no variable (i.e. filedescriptor) which points to STDERR anymore. Since we can only access it by a filedescriptor we now have "lost" STDERR. And that is why at this point
1>&2
will not achieve our intended switching of STDOUT and STDERR. To stay in the world of c: We are assigning to the variable fd1 the same object that fd2 is pointing to. Since we already pointed fd2 to fd1, we are now pointing fd1 to STDOUT again. No change at all happened.
Have a look at the first statement again:
configure_commands
3>&1 1>&2 2>&3 | tee "$HOME/configure.err"
Code:
3>&1 implicitly creates an additional filedescriptor and points it to fd1. This what our table looks like now
pointer to Object
fd1 ---> STDOUT
fd2 ---> STDERR
fd3 ---> STDOUT
1>&2:
pointer to Object
fd1 ---> STDERR
fd2 ---> STDERR
fd3 ---> STDOUT
2>&3:
pointer to Object
fd1 ---> STDERR
fd2 ---> STDOUT
fd3 ---> STDOUT
At no point one of the streams got lost. They were always referenced by at least one file descriptor.
There is at least one other (right) way than
3>&1 1>&2 2>&3
to arrange these redirections and end up with the same result (switch fd1 and fd2). You might want to try to find it out.
Hope this helps.