bash 中的重定向是分层的吗?

Is the redirect in bash hierarchical?

bash as

的手册中有一个例子解释了重定向命令的顺序将如何影响最终结果

Note that the order of redirections is significant. For example, the command

ls > dirlist 2>&1

directs both standard output (file descriptor 1) and standard error (file descriptor 2) to the file dirlist, while the command

ls 2>&1 > dirlist

directs only the standard output to file dirlist, because the standard error was made a copy of the standard output before the standard output was redirected to dirlist.

在第二种情况下,如果我的理解是正确的,2>&1 意味着 stderr 中的所有内容将被重定向到 stdout> dirlist 意味着 [=16] 中的所有内容=] 将被重定向到文件 dirlist。最后的结果是控制台只显示stderr

我的问题是,出现这种现象是不是说明bash的重定向不是分层的?因为如果它是分层的,stderr 已经被放入 stdout 并且应该与 stdout.

一起放入文件 dirlist

您需要看到“>&n”(iow,“1>&n”),因为“fd 1 被重定向到 fd n 当前 要去的任何地方”,not “fd 1 会去到 fd n 要去的地方,即使 fd n 在那之后发生变化”。因此,我不喜欢“复制”这个词,因为它对我来说有点模棱两可:'n>&m' make fd n 'point' to where fd m currently points to (and does不是 link fd n 到 fd m 以任何方式,即它不会使它们成为“相同的 fd”。它们仍然是不同的 fd,可以独立更改)

ex1 :在交互式 shell 中,stdout 和 stderr 都转到您的终端: 默认情况下,您在此 shell 中键入的每个命令也会将它们的 STDOUT (fd 1) 输出到终端,并将它们的 STDERR (fd 2) 输出到终端(除非您告诉它们输出到其他东西)

因此,当您将命令重定向到其他地方输出时:

command 2>&1 >/dev/null 
   # now `command`'s STDERR (its `fd 2`) goes to: the terminal (again), as by default its `fd 1` goes to terminal
   # and then only its `fd 1` (its stdout) is redirected to /dev/null, discarding it.

command >/dev/null 2>&1 
   # here, fd 1 (stdout) is redirected to /dev/null
   # and then fd 2 (stderr) goes to where fd 1 is *currently* going, ie /dev/null too

大多数带有“>&something”或“>something”的命令都将此重定向设为临时。一个特例是 'exec >something' 或 'exec >&something',它改变了当前 shell 或程序的重定向,因此保持有效“直到另一个 exec 再次改变它”或直到当前 shell 或程序结束。 iow,'exec someredirection' 影响的不是子命令,而是当前的 program/shell.

ex2 : 在用 program >/some/log_stdout 2>/some/log_stderr

启动的程序中
# beginning of the program
... # anything here will have its stdout go to /some/log_stdout and stderr go to /some/log_stderr
exec 3>&1 # cree a new fd, 3, that goes to where stdout currently goes (/some/log_stdout)
exec 4>&2 # cree a new fd, 4, that goes to where stderr currently goes (/some/log_stderr)
exec 1>/dev/null # redirect fd 1 to /dev/null. fd 3 still goes to /some/log_stdout
exec 2>/dev/null # redirect fd 2 to /dev/null. fd 4 still goes to /some/log_stderr
something # here, no output: this command "something" outputs to fd1 and fd2, both going to /dev/null
other thing # same for that one
exec 5>&1 # this new fd, 5, goes to where fd1 currently goes (/dev/null)
somecommand ... >&3
  # here, the STDOUT (only) of somecommand goes to where fd 3 currently points to, 
  # ie to /some/log_stdout  (and of course just for the durection of somecommand)
another_command 
  # no output, as it outputs by default its stdout to fd 1 and stderr to fd 2, both going to /dev/null
# etc.
exec 1>&3  # fd 1 now goes to wherever fd3 was going, 
 # and thus we "restore" the original fd 1
 # (as no 'exec 3>...' where done to change fd 3) 
exec 2>&4  # same thing for fd 2, going to where fd 4 is going 
   # (which was where fd 2 was originally going)

“想象”以下 table 附加到您的命令(或您的 shell,或您的脚本:

您的交互式 shell 通常默认为:

fd 1(=stdout)  _goes_to_  your terminal
fd 2(=stderr)  _goes_to_  your terminal

它启动的每个命令也将具有 'a copy of this table'(或者更确切地说,它们的进程 space 继承启动 command/shell 的当前 fd),除非另有说明。

启动时:somecommand >/dev/null 2>&1,在启动某个命令之前,fd 1 首先指向“/dev/null”,然后 fd 2 指向 fd 1 现在指向的位置(/dev/null) 并使用 'table':

启动进程
fd 1(=stdout)  _goes_to_  /dev/null
fd 2(=stderr)  _goes_to_  /dev/null

启动命令的shell仍然有自己的table,没有变化。 (除非 'somecommand' 实际上是 'exec n>somewhere',它将当前 shell 的进程 fd n 更改为指向 'somewhere',“永久地”(好吧,直到当前 shell 完成。).