You got a few very good answers already. Let me stress though that there are two different concepts involved here, the understanding of which helps tremendously:
Background: File descriptor vs. file table
Your file descriptor is just a number 0...n, which is the index in the file descriptor table in your process. By convention, STDIN=0, STDOUT=1, STDERR=2 (note that the terms STDIN
etc. here are just symbols/macros used by convention in some programming languages and man pages, there is not an actual "object" called STDIN; for the purpose of this discussion, STDIN is 0, etc.).
That file descriptor table in itself does not contain any information whatsoever about what the actual file is. Instead, it contains a pointer to a different file table; the latter contains information about an actual physical file (or block device, or pipe, or whatever else Linux can address via the file mechanism) and more information (i.e., whether it's for reading or writing).
So when you use >
or <
in your shell, you simply replace the pointer of the respective file descriptor to point to something else. The syntax 2>&1
simply points descriptor 2 to wherever 1 points. > file.txt
simply opens file.txt
for writing and lets STDOUT (file decsriptor 1) point to that.
There are other goodies, e.g. 2>(xxx)
(i.e.: create a new process running xxx
, create a pipe, connect file descriptor 0 of the new process to the reading end of the pipe and connect the file descriptor 2 of the original process to the writing end of the pipe).
This is also the basis for "file handle magic" in other software than your shell. For example, you could, in your Perl script, dup
licate the STDOUT file descriptor into another (temporary) one, then re-open STDOUT to a newly created temporary file. From this point on, all STDOUT output from your own Perl script and all system()
calls of that script will end up in that temporary file. When done, you can re-dup
your STDOUT to the temporary descriptor you saved it to, and presto, all is as before. You can even write to that temporary descriptor meanwhile, so while your actual STDOUT output goes to the temporary file, you can still actually output stuff to the real STDOUT (commonly, the user).
Answer
To apply the background information given above to your question:
In what order does the shell execute commands and stream redirection?
Left to right.
<command> > file.txt 2>&1
fork
off a new process.
- Open
file.txt
and store its pointer in the file descriptor 1 (STDOUT).
- Point STDERR (file descriptor 2) to whatever the fd 1 points to right now (which again is the already opened
file.txt
of course).
exec
the <command>
This apparently redirects stderr to stdout first, and then the resulting stdout is redirected to file.txt.
This would make sense if there were only one table, but as explained above there are two. File descriptors are not pointing to each other recursively, it makes no sense to think "redirect STDERR to STDOUT". The correct thought is "point STDERR to wherever STDOUT points". If you change STDOUT later, STDERR stays where it is, it does not magically go along with further changes to STDOUT.
bash
manual. By the way, redirections are not commands.execv
-family syscall is invoked to actually hand the subprocess off to the command being started, the shell is then out of the loop (no longer has code executing in that process) and has no possible way to control what happens from that point on; redirections thus must all be performed before execution begins, while the shell has a copy of itself running in the process itfork()
ed to run your command in.