是什么让 shell 将这些重定向解释如下?

是什么让 shell 将这些重定向解释如下?

谁能帮助我理解 shell 如何解析重定向的这种看似奇怪的行为..?

$ cat > test.txt
Line 1
Line 2
$ ls -i dummy.txt dummy2.txt
ls: dummy.txt: No such file or directory
ls: dummy2.txt: No such file or directory
$ 
$ cat test.txt > dummy.txt > dummy2.txt
$ cat dummy2.txt
Line 1
Line 2
$ cat dummy.txt
$

有趣的!我本来期望 from 的输出cat test.txt被重定向到dummy.txt,然后 shell 会读取dummy.txt为 的输入dummy2.txt。但不知怎的,外壳以不同的方式解析它......

那么如何完全绕过 的内容test.txt作为dummy2.txt,的输入dummy.txt呢?


笔记: bash 和 ksh 中的结果相同..

答案1

重要的是要知道您的 shell 不会重定向输出,但是文件描述符。 POSIX 兼容操作系统上的每个进程都有 3 个 I/O 流:STDIN、STDOUT 和 STDERR。重定向>会重定向 STDOUT 流的文件描述符。所以 shell 在解析这一行时看到的是:

  1. cat test.txt- 好的,我将运行该程序cat并给出参数test.txt
  2. > dummy.txt- 哦,现在我将获取进程的 STDOUT 描述符cat并将其连接到名为“dummy.txt”的文件。我不在乎之前是否有任何内容,我将创建该文件并将其截断为零。
  3. > dummy2.txt- 哦,现在我将获取进程的 STDOUT 描述符cat(我之前连接到“dummy.txt”)并将其连接到另一个新文件。我不能同时做这两件事,因为你正在重定向,而不是重复文件描述符。

这就是为什么这个命令:

find / 2>&1 >/dev/null

导致 STDOUT 上出现错误输出,并且没有常规 (STDOUT) 输出,但是这个:

find / >/dev/null 2>&1

结果根本没有输出。在第一种情况下,我们将 STDERR 放在 STDOUT 的副本(dup)上,然后将旧的 STDOUT 放入垃圾桶中。在第二个中,我们首先将 STDOUT 放入垃圾箱,然后复制它(仍然指向/dev/null)并将 STDERR 也放在那里。

答案2

从评论到答案,尽管其中一些是猜测:

如果你在 bash 中运行,

cat test.txt > d1 > d2 > d3 > d4 > d5 

那么d1到d4将为空,test.txt的内容将在d5中。 Bash 似乎只对最后指定的文件执行实际的重定向。

奇怪的是,zsh(至少版本 5)也会将 test.txt 的内容复制到所有中间文件中。

bash 手册页确实表明重定向的顺序很重要,因此最后指示的方向似乎覆盖了中介。但是,因为它们在那里,所以 bash 在注意到实际内容的去向之前就创建了这些文件。

手册页的具体部分内容如下:

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

              ls > dirlist 2>&1

directs both standard output and standard error to the file dirlist, 
while the command

              ls 2>&1 > dirlist

directs  only the standard output to file dirlist, because the standard 
error was duplicated from the standard output before the standard output
was redirected to dirlist.

这并没有具体涵盖当前的情况,但确实表明订单确实很重要。因此,在做奇怪的事情时看到奇怪的行为(反复将标准输出重定向到不同的地方)可能会产生令人惊讶的结果。

我相信很快就会有人纠正这个猜测中的任何疯狂(或轻微)错误:-)

相关内容