谁能帮助我理解 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 在解析这一行时看到的是:
cat test.txt
- 好的,我将运行该程序cat
并给出参数test.txt
> dummy.txt
- 哦,现在我将获取进程的 STDOUT 描述符cat
并将其连接到名为“dummy.txt”的文件。我不在乎之前是否有任何内容,我将创建该文件并将其截断为零。> 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.
这并没有具体涵盖当前的情况,但确实表明订单确实很重要。因此,在做奇怪的事情时看到奇怪的行为(反复将标准输出重定向到不同的地方)可能会产生令人惊讶的结果。
我相信很快就会有人纠正这个猜测中的任何疯狂(或轻微)错误:-)