问题 cmd> / dev / null 2>&1如何工作?


我正在阅读重定向数据 /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 代表 stderr 和 1 代表 stdout。因此,如果我这样阅读,看起来我正在创建一个 stderr 文件和重定向 stdout 对吗?

如果是这样的话,那是什么呢 & 在那个命令吗?


12669
2018-06-08 02:29


起源

&让bash知道 1 不是文件名,但是 stderr。顺便说一句,你不必使用 |, ping a.b.c 2>/dev/null 要么 ping a.b.c &>/dev/null 应该这样做。 - Jokester
@jokester谢谢!因此,如果 1 不是文件名,是什么?只是记忆? - hax0r_n_code
你应该得到“/ dev / null:Permission denied”,因为你正在尝试执行 /dev/null 作为一个命令。它应该是 > /dev/null不是 | /dev/null。 - Keith Thompson
@nkon他们称之为 文件描述符和shell使用相同的对流。 - Jokester
请注意,您可以写: ping abc.example.com 2>/dev/null 抛弃错误消息而不丢弃标准输出。 - Jonathan Leffler


答案:


你是对的, 2 是 STDERR1 是 STDOUT。当你这样做 2>&1 你是说:“打印到 STDOUT (1)将会发生的事情 STDERR (2)“。之前,你说过你的 STDOUT 会去的 /dev/null。因此,没有看到任何东西。在示例1和2中,您将获得输出消息,因为它正在打印到 STDERR,因为常规重定向只重定向 STDOUT

当你进行重定向时,你不会创建一个 STDERR,过程总是有一个 STDERR 和a STDOUT 什么时候创建它们。


12
2018-06-08 02:35



@nkon如果你想,你可以 看到 哪些文件描述符附加到进程。要做到这一点,找到pid - 例如, pgrep firefox。然后 ls -l /proc/<pid>/fd  - 注意fds实际上表示为目录中的符号链接!我仍然觉得这一切都令人着迷,自2002年以来我一直在运行Debian。 - cmt


考虑下面的代码,它将单词“stdout”打印到stdout,将单词“stderror”打印到stderror。

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

请注意,'&'运算符告诉bash 2是文件描述符(指向stderr)而不是文件名。如果我们省略'&',则会打印此命令 stdout 到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本身!!!!(见 指针上的这个资源 了解更多信息)。由于文件描述符1指向/ dev / null,并且文件描述符2指向与文件描述符1相同的文件,因此文件描述符2现在也指向/ dev / null。因此,两个文件描述符都指向/ dev / null,这就是没有输出的原因。


要测试您是否真的理解这个概念,请在切换重定向顺序时尝试猜测输出:

(echo "stdout"; echo "stderror" >&2)  2>&1 >/dev/null

 stderror

这里的推理是从左到右进行评估,Bash看到2>&1,因此将文件描述符2设置为指向与文件描述符1相同的位置,即stdout。然后它设置文件描述符1(记住> / dev / null = 1> / dev / null)指向> / dev / null,从而删除通常发送到标准输出的所有内容。因此我们剩下的就是那些没有发送到子shell中的stdout(括号中的代码) - 即“stderror”。   有趣的是要注意,尽管1只是指向stdout的指针,但是将指针2重定向到1 2>&1不形成指针链2 - > 1 - > stdout。如果确实如此,则将代码重定向到/ dev / null 2>&1 >/dev/null 会给指针链2 - > 1 - > / dev / null,因此代码不产生任何东西,与我们上面看到的相反。


最后,我注意到有一种更简单的方法可以做到这一点:

从第3.6.4节 这里,我们看到我们可以使用运算符 &> 重定向stdout和stderr。因此,要将任何命令的stderr和stdout输出重定向到 \dev\null (删除输出),我们只需输入 $ command &> /dev/null  或者在我的例子中:

$ (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文档文件描述符表的解释指针简介


0
2017-11-19 01:34