12

我正在阅读将数据重定向到的内容/dev/null,因此我尝试了一个简单的测试:

ping a.b.c  # which results in an address not found

如果我试试这个:

ping a.b.c > /dev/null # prints the same error message as the one above

但是,如果我这样做:

ping a.b.c > /dev/null 2>&1 # The error message is gone

最后一个解决方案是理想的解决方案,但这是怎么回事2>&1?到目前为止,我的研究表明2表示stderr1表示stdout。所以如果我这样读,看起来我正在创建一个stderr文件并重定向stdout到它?

如果是这种情况,那么&该命令的作用是什么?

4

2 回答 2

12

你是对的,2STDERR1STDOUT。当你这样做时,2>&1你是在说:“打印到STDOUT( 1) 将去 () 的东西STDERR2。在那之前,你说你STDOUT会去/dev/null。因此,什么也看不见。在示例 1 和 2 中,您会收到输出消息,因为它正在被打印到STDERR,因为常规重定向仅重定向STDOUT

而且,当您进行重定向时,您并没有创建 a STDERR,进程在创建时始终具有 aSTDERR和 a STDOUT

于 2013-06-08T02:35:59.347 回答
1

考虑以下代码,它将单词“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 文档文件描述符表的解释,指针简介

于 2017-11-19T01:34:58.297 回答