【问题标题】:Why does 2>&1 need to come before a | (pipe) but after a "> myfile" (redirect to file)?为什么 2>&1 需要在 | 之前(管道)但在“> myfile”之后(重定向到文件)?
【发布时间】:2012-07-19 05:32:06
【问题描述】:

当将 stderr 与 stdout 结合使用时,为什么 2>&1 需要在 |(管道)之前,而在 > myfile(重定向到文件)之后?

将 stderr 重定向到 stdout 以进行文件输出:

  echo > myfile 2>&1

将标准错误重定向到管道的标准输出:

  echo 2>&1 | less



我的假设是我可以这样做:

  echo | less 2>&1 

它会起作用,但它不会。为什么不呢?

【问题讨论】:

    标签: bash shell pipe stdout pipeline


    【解决方案1】:

    我的答案是理解文件描述符。每个进程都有一堆文件描述符:打开的文件的条目。默认情况下,数字 0 用于标准输入,数字 1 用于标准输出,数字 2 用于标准错误。

    i/o 重定向器 > 和 foo > bar),在启动进程“foo”时,打开文件“bar”以进行写入并连接到文件描述符编号 1。如果您只希望“bar”中的 stderr ,您将使用 foo 2> bar 打开文件栏并将其挂钩到标准错误。

    现在是 i/o 重定向器“2>&1”。我通常将其读取为'将文件描述符 2 与文件描述符 1 相同。在从左到右读取命令行时,您可以执行以下操作:foo 1>bar 2>&1 1>/dev/tty。这样,文件描述符 1 设置为文件“bar”,文件描述符 2 设置为与 1 相同(因此为“bar”),之后,文件描述符 1 设置为 /dev/tty。正在运行的 foo 正在将其输出发送到 /dev/tty 并将其 stderr 发送到文件 'bar'。

    现在管道进来了:这不会改变文件描述符,但是,它将在进程之间连接它们:左进程的标准输出和下一个进程的标准输入。 Stderr 被传递。因此,如果您希望管道仅在 stderr 上工作,则使用foo 2| bar,它将foo 的stderr 连接到bar 的stdin。 (我不确定foo 的标准输出会发生什么。)

    如上,如果你使用foo 2>&1 | bar,由于foo的stderr被重新路由到foo的stdout,foo的stdout和stderr都到达bar的stdin。

    【讨论】:

      【解决方案2】:

      管道是由|分隔的命令列表。您指定的任何重定向都适用于组成命令(简单或复合),但不适用于整个管道。每个管道通过隐式将重定向应用到每个子shell与命令关联的任何重定向被评估,将一个命令的标准输出链接到下一个命令的标准输入。

      cmd 2>&1 | less
      

      第一个子shell 的第一个标准输出被重定向到less 正在读取的管道。接下来,2>&1 重定向应用于第一个命令。将 stderr 重定向到 stdout 有效,因为 stdout 已经指向管道。

      cmd | less 2>&1
      

      这里,重定向适用于less。 Less 的 stdout 和 stderr 大概都开始指向终端,所以 2>&1 在这种情况下没有效果。

      如果您希望重定向应用于整个管道、将多个命令组合为管道的一部分或嵌套管道,请使用命令组(或任何其他复合命令):

      { { cmd1 >&3; cmd2; } 2>&1 | cmd3; } 3>&2
      

      可能是一个典型的例子。最终结果是:cmd1cmd2 的 stderr -> cmd3; cmd2 的标准输出 -> cmd3;和cmd1cmd3 的标准错误,以及cmd3 的标准输出-> 终端。

      如果您使用 Bash 特定的 |& 管道,事情就会变得奇怪,因为每个管道的标准输出重定向仍然首先发生,但标准错误重定向实际上是最后发生的。比如:

      f() { echo out; echo err >&2; }; f >/dev/null |& cat
      

      现在,与直觉相反,所有输出都被隐藏了。 f 的第一个标准输出进入管道,f 的下一个标准输出被重定向到/dev/null,最后,stderr 被重定向到标准输出(/dev/null 仍然)。

      我建议不要在 Bash 中使用 |& —— 它在这里用于演示。

      【讨论】:

      【解决方案3】:

      添加到 ormaaj 的答案:

      您需要以正确的顺序指定重定向运算符的原因是它们是从左到右进行评估的。考虑这些命令列表:

      # print "hello" on stdout and "world" on stderr
      { echo hello; echo world >&2; }
      
      # Redirect stdout to the file "out"
      # Then redirect stderr to the file "err"
      { echo hello; echo world >&2; } > out 2> err
      
      # Redirect stdout to the file "out"
      # Then redirect stderr to the (already redirected) stdout
      # Result: all output is stored in "out"
      { echo hello; echo world >&2; } > out 2>&1
      
      # Redirect stderr to the current stdout
      # Then redirect stdout to the file "out"
      # Result: "world" is displayed, and "hello" is stored in "out"
      { echo hello; echo world >&2; } 2>&1 > out
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-30
        • 1970-01-01
        • 1970-01-01
        • 2015-08-19
        • 1970-01-01
        • 2023-03-03
        • 1970-01-01
        • 2021-09-04
        相关资源
        最近更新 更多