您已经得到了一些非常好的答案。让我强调一下,尽管这里涉及两个不同的概念,但对它们的理解会极大地帮助您:
背景:文件描述符与文件表
您的文件描述符只是一个数字0 ... n,它是您进程中文件描述符表中的索引。按照约定,STDIN = 0,STDOUT = 1,STDERR = 2(请注意,STDIN
此处的术语等只是某些编程语言和手册页中约定使用的符号/宏,没有称为STDIN的实际“对象”;对于本讨论的目的,STDIN 为 0等)。
该文件描述符表本身不包含任何有关实际文件的信息。相反,它包含一个指向其他文件表的指针。后者包含有关实际物理文件(或块设备,管道或Linux可以通过文件机制寻址的任何其他内容)的信息,以及更多信息(即,是否用于读取或写入)。
因此,当在外壳中使用>
或<
时,只需替换相应文件描述符的指针以指向其他对象即可。该语法2>&1
只是将描述符2指向1的任何位置。> file.txt
只需打开即可file.txt
进行写操作,并让STDOUT(文件描述符1)指向该文件。
还有其他好处,例如2>(xxx)
(例如:创建一个正在运行的新进程xxx
,创建一个管道,将新进程的文件描述符0连接到管道的读取端,并将原始进程的文件描述符2连接到该进程的写入端。管)。
这也是Shell以外的其他软件中“文件处理魔术”的基础。例如,您可以在Perl脚本dup
中将STDOUT文件描述符链接到另一个(临时)文件描述符中,然后将STDOUT重新打开到新创建的临时文件中。从这一点开始,您自己的Perl脚本的所有STDOUT输出以及system()
该脚本的所有调用将最终存储在该临时文件中。完成后,您可以dup
将STDOUT 重新保存到保存到其中的临时描述符,并且所有操作都像以前一样。您甚至可以同时写入该临时描述符,因此,即使您实际的STDOUT输出进入临时文件,您仍然可以将内容实际输出至实际的 STDOUT(通常是用户)。
回答
要将以上给出的背景信息应用于您的问题:
Shell以什么顺序执行命令和流重定向?
左到右。
<command> > file.txt 2>&1
fork
一个新的过程。
- 打开
file.txt
其指针并将其存储在文件描述符1(STDOUT)中。
- 将STDERR(文件描述符2)指向fd 1现在指向的任何位置(
file.txt
当然,这也是已经打开的)。
exec
的 <command>
显然,这首先将stderr重定向到stdout,然后将生成的stdout重定向到file.txt。
如果只有一个表,这将是有意义的,但是如上所述,有两个表。文件描述符不是递归指向彼此的,因此认为“将STDERR重定向到STDOUT”是没有意义的。正确的想法是“将STDERR指向STDOUT指向的任何位置”。如果稍后更改STDOUT,则STDERR会保留在原位置,不会神奇地伴随对STDOUT的进一步更改。