考虑以下代码,它将单词“stdout”打印到stdout,将单词“stderror”打印到stderror。
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
请注意,'&' 运算符告诉 bash 2 是文件描述符(指向 stderr)而不是文件名。如果我们省略了 '&',此命令将打印stdout
到标准输出,并创建一个名为“2”的文件并stderror
在那里写入。
通过试验上面的代码,您可以自己了解重定向运算符的工作原理。例如,通过更改两个描述符 1,2 中的哪个文件被重定向到/dev/null
以下两行代码,分别删除 stdout 中的所有内容和 stderror 中的所有内容(打印剩余的内容)。
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
现在,我们接近问题的症结所在(用我的例子代替你的例子),为什么
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
不产生输出?要真正理解这一点,我强烈建议您阅读有关文件描述符表的网页。假设您已经完成了阅读,我们可以继续。请注意,Bash 从左到右处理;因此 Bash>/dev/null
首先看到(与 相同1>/dev/null
),并将文件描述符 1 设置为指向 /dev/null 而不是 stdout。完成此操作后,Bash 向右移动并看到2>&1
。这会将文件描述符 2 设置为指向与文件描述符 1 相同的文件(而不是指向文件描述符 1 本身!!!!(请参阅this resource on pointers)了解更多信息)。由于文件描述符 1 指向 /dev/null,并且文件描述符 2 指向与文件描述符 1 相同的文件,因此文件描述符 2 现在也指向 /dev/null。因此,两个文件描述符都指向 /dev/null,这就是不呈现输出的原因。
为了测试你是否真的理解这个概念,试着猜测我们切换重定向顺序时的输出:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
标准错误
这里的原因是,从左到右计算,Bash 看到 2>&1,因此将文件描述符 2 设置为指向与文件描述符 1 相同的位置,即 stdout。然后它将文件描述符 1(记住 >/dev/null = 1>/dev/null)设置为指向 >/dev/null,从而删除通常发送到标准输出的所有内容。因此,我们只剩下没有发送到子shell 中的stdout 的内容(括号中的代码)——即“stderror”。有趣的是,即使 1 只是一个指向 stdout 的指针,将指针 2 重定向到 1 via2>&1
不会形成指针链 2 -> 1 -> stdout。如果是这样,由于将 1 重定向到 /dev/null,代码2>&1 >/dev/null
将给出指针链 2 -> 1 -> /dev/null,因此代码不会生成任何内容,与我们在上面看到的相反。
最后,我注意到有一种更简单的方法可以做到这一点:
从这里的第 3.6.4 节,我们看到我们可以使用操作符&>
来重定向 stdout 和 stderr。因此,要将任何命令的 stderr 和 stdout 输出重定向到\dev\null
(删除输出),我们只需键入
$ command &> /dev/null
or 在我的示例中:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
关键要点:
- 文件描述符的行为类似于指针(尽管文件描述符与文件指针不同)
- 将文件描述符“a”重定向到指向文件“f”的文件描述符“b”,导致文件描述符“a”指向与文件描述符 b 相同的位置 - 文件“f”。它不会形成指针链 a -> b -> f
- 由于上述原因,顺序很重要,
2>&1 >/dev/null
是 != >/dev/null 2>&1
。一个产生输出,另一个不产生!
最后看看这些很棒的资源:
关于重定向的 Bash 文档,文件描述符表的解释,指针简介