> /dev/null 2>&1 是如何工作的?

> /dev/null 2>&1 是如何工作的?

我了解重定向输出如何> /dev/null导致它不打印到屏幕。

但由于某种原因,这并不总是足够的,有些东西仍然会被打印出来。

在这些情况下> /dev/null 2>&1就会达到预期的结果。

然而这让我有点困惑。有人能详细解释一下这是如何工作的吗?

特别令人困惑的是这&1部分。如果我看到,&我会想到“在后台运行”。不知道1是什么意思,如果是2呢?

答案1

shell 运行的程序获得三个流:

0 - standard input [stdin]
1 - standard output [stdout]
2 - standard error (output) [stderr]

您可以将其视为stdin键盘(没有管道或重定向,这是一种简化)。

然后,要在屏幕上打印内容,每个程序都可以写入标准输出或标准错误,通常正常输出转到stdout,错误输出到stderr

当您重定向时,>您只是重定向标准输出。你可以使用1>.

当您重定向时,2>您只是重定向 stderr。

因此,如果你的程序打印了一些东西stderr,而你只做了:

program > /dev/null

你仍然会看到它。

您至少有两种解决方案来避免看到stderr输出,重定向stdoutstderr/dev/null

program > /dev/null 2>/dev/null

或者,这是您问题的答案,重定向stderrstdout,它已经重定向到/dev/null

program > /dev/null 2>&1

这就是2>&1重定向stderrstdout. Bash 从右向左阅读,这就是它出现在最后的原因。

答案2

您需要花一些时间才能理解 I/O 重定向的复杂性:我认为要点是:

  1. 文件描述符编号 、012是诸如xyzm 等变量,或者可能是数组的索引,例如 、fd[0]fd[1]fd[2]...
  2. 重定向运算符<>>><&>&是为这些变量赋值的运算符。
  3. 这些分配在命令行中严格从左到右执行,无论命令出现在命令行中的什么位置,都会执行该命令。
  4. 如果从左侧省略文件描述符编号,则至少对于前三个运算符有一个默认值0或。1

这可能还不足以完全澄清问题,所以让我们通过几个例子来进行说明。


cmd arg arg arg。这里我们没有显式的重定向,但是这个简单的示例有助于建立 shell 执行操作的基本过程。那么,这里发生了什么?

  1. shell 分叉一个新进程,仍在 shell 中执行代码,最终将运行cmd,但尚未运行。从这里开始的一切都发生在新的流程中。
  2. 设置默认 i/o 重定向:这些大致相当于显式重定向0</dev/tty 1>/dev/tty 2>/dev/tty
  3. 在这里的中间,任何来自命令行的明确重定向都将被处理,可能会修改已经建立的默认分配。
  4. 在已经分叉的进程中运行cmd arg arg arg,保留已设置的文件描述符关联。 (这是通过exec系统调用的某些变体完成的。)

那么,那又怎样呢cmd arg arg arg >file

  1. 分叉一个新进程,并设置默认的文件描述符分配。
  2. 找到第一个显式重定向,>file
    • 将其转换为等效但很少见的形式1>file
    • 打开file输出并附加到文件描述符1。请注意,这会覆盖之前在步骤 (1) 中完成的 stdout 默认分配。
  3. cmd arg arg arg在同一进程中运行。文件描述符1被修改,因此 stdoutfile代替终端。

但现在,假设我们获得了大量输出,而我们只想将其丢弃,而不是将其保存在文件中。嗯,我们想要cmd arg arg arg>/dev/null

  • 这是确切地在所有方面都与上面的示例相同,除了 /dev/null 是一个始终存在的神奇文件,它接受您想要提供给它的所有字节,然后将它们丢弃。

好吧,一堆输出仍然出现。为什么上面的重定向没有修复它?嗯,特别是历史上,许多 UNIX 命令将正常输出和错误输出分开。前者转到 stdout(文件描述符 1),后者转到 stderr(文件描述符 2)。这通常很方便,但天哪,你只是想让输出消失。

你做什么工作?好吧,不知何故,您还需要将所有发送到 stderr 的字节重新分配(重新定向)到 /dev/null。好吧,以下工作:

cmd arg arg arg >/dev/null 2>/dev/null

(请注意,这并不是您一开始提出的问题。很快就会出现。另外,请注意这种形式:由于 /dev/null 的神奇属性,它只会执行您想要的操作。)

好的。这里发生了什么?

  1. 与往常一样,分叉一个新进程,并设置默认的文件描述符分配。
  2. 找到第一个显式重定向,>/dev/null
    • 将其转换为等效但很少见的形式1>/dev/null
    • 打开/dev/null输出并附加到文件描述符1。请注意,这会覆盖之前在步骤 (1) 中完成的 stdout 默认分配。
  3. 查看下一个显式重定向 ' 2>/dev/null
    • 打开/dev/null以进行输出并将该打开的文件附加到文件描述符2。这将覆盖附加到描述符的原始文件2
  4. cmd arg arg arg在同一进程中运行。文件描述符12都已从默认设置进行了修改。

重要的是要认识到 的有两个单独的开口/dev/null。文件描述符12.如果您用常规文件名替换 /dev/null 以尝试捕获文件中的所有输出,这一事实会导致麻烦。


最后,在这一切之后,您实际询问的表格。

cmd arg arg arg >/dev/null 2>&1

  1. 与往常一样,分叉一个新进程,并设置默认的文件描述符分配。
  2. 找到第一个显式重定向,>/dev/null
    • 将其转换为等效但很少见的形式1>/dev/null
    • 打开/dev/null输出并附加到文件描述符1。请注意,这会覆盖之前在步骤 (1) 中完成的 stdout 默认分配。
  3. 查看下一个显式重定向 ' 2>&1
    • >&表示分配给2当前分配给的同一打开文件1。即,它就像y = xfd[2] = fd[1]
  4. cmd arg arg arg在同一进程中运行。文件描述符1和都已2从默认设置进行了修改,但现在 fd 1 和 fd 2 都指向相同的打开文件。

在本例中,我们只打开 /dev/null 一次,而不是像上一个示例中那样打开两次。因为 /dev/null 只是丢弃输出,所以这里的差异并不重要。

那么什么时候重要呢?


万岁,你已经摆脱了全部输出。但过了一段时间,您有点希望将所有输​​出按照您在屏幕上看到的顺序保存在一个文件中。

忽略我之前的警告,你从看似简单的开始

cmd arg arg arg >file 2>file

这就像我之前描述的那样工作,但是当你查看内部时,file你会发现 stout 和 stderr 奇怪地交错,可能就像 stderr 的位覆盖了文件的前面。卧槽?

通过使用此表格,您打开了file两次。嗯,呃...但是,每个打开的实例file都有一个独立的输出位置,并且它们都从零开始。因此,当 stdout 提前产生兆字节的输出时,stderr 的输出位置保持为零,并且仅在偶尔生成错误消息时才向前移动。


最后,在单个文件中捕获 stderr 和 stdout 的正确方法。

cmd arg arg arg >file 2>&1

这与后面几个例子的解释完全相同。与之前不正确的形式的重要区别是 stdout 和 stderr 都引用相同的打开 的实例file,等等分享一个输出位置。

答案3

简而言之:

  • > /dev/null意味着将标准输出重定向到/dev/null(不打印它)。
  • 2> &1表示将 stderr 重定向到 stdout (已重定向到/dev/null)。

但由于某种原因,这并不总是足够的,有些东西仍然会被打印出来。

以防万一您不熟悉,东西可以发送到两个地方之一:

  • 文件描述符 1 是标准输出。这是应用程序输出内容时通常使用的内容。
  • 文件描述符 2 是 stderr。这种情况不太常见,但只要发生错误,应用程序通常会打印到 stderr。

在大多数 shell 中,默认情况下都会在 shell 中打印 stdout 和 stderr。当您使用时,> /dev/null您将标准输出定向到虚拟接收器,因此外壳不再打印标准输出。但是,stderr 没有受到影响。您所说的“出于某种原因”是应用程序写入 stderr 的情况。

相关内容