【问题标题】:Shell redirection i/o orderShell 重定向 i/o 命令
【发布时间】:2021-11-09 22:17:50
【问题描述】:

我正在玩 i/o shell 重定向。我尝试过的命令(在 bash 中):

ls -al *.xyz 2>&1 1> files.lst

ls -al *.xyz 1> files.lst 2>&1

当前文件夹中没有任何*.xyz 文件。

这些命令给了我不同的结果。第一个命令在屏幕上显示错误消息ls: *.xyz: No such file or directory。但是第二个将此错误消息打印到文件中。为什么第一个命令无法将 err 输出写入文件?

【问题讨论】:

    标签: bash shell stdout stderr io-redirection


    【解决方案1】:

    Bash manual 有一个清晰的示例(与您的类似),以表明顺序很重要并解释了差异。以下是摘录的相关部分(重点是我的):

    请注意,重定向的顺序很重要。例如, 命令

    ls > 目录列表 2>&1

    同时指示标准输出(文件描述符 1)和标准错误 (文件描述符 2)到文件 dirlist,而命令

    ls 2>&1 > 目录

    仅将标准输出定向到文件 dirlist,因为 标准 错误是在标准之前复制了标准输出 输出被重定向到 dirlist

    This post 从 POSIX 的角度解释它。

    由于关键差异而发生混乱。 > 重定向,方法是使左操作数 (stderr) 指向右操作数 (stdout),但通过复制右操作数并将其分配给左侧。从概念上讲,通过复制而不是通过引用分配。

    所以从左到右读取这就是 Bash 的解释方式:ls > dirlist 2>&1 意味着将stdout 重定向到文件dirlist,然后将stderr 重定向到当前stdout 的任何内容(这已经是文件dirlist)。但是,ls 2>&1 > dirlist 会将stderr 重定向到当前的stdout(即屏幕/终端),然后将stdout 重定向到dirlist

    【讨论】:

      【解决方案2】:

      重定向是:

      • 从左到右处理。
      • 迭代解释:
        • 较早重定向会影响较晚重定向:
          • 如果较早的重定向已重定向给定的流(由文件描述符编号标识,例如1 用于stdout(默认值),2 用于stderr),以后针对该流的重定向指的是已经-重定向版本。
        • 反之亦然 - 稍后的重定向对先前重定向的目标没有追溯影响:
          • 例如,如果您在较早的重定向中指定文件描述符1 作为目标,那么1 的含义当时 被锁定,即使1 稍后被重定向。
      • 但是请注意,在所有重定向到位之前,实际上不会发送输出,并且在命令执行之前创建或截断任何重定向目标输出文件开始(这就是您无法使用单个命令读取并将输出重定向到同一文件的原因)。

      应用于问题中的示例:

      • >file 2>&1:

        • >file first 重定向标准输出(文件描述符 1,暗示不使用文件描述符编号前缀 >)到输出文件 file
        • 2>&1然后将标准错误(2)重定向到已经重定向的标准输出(1)。
        • 最终效果是两个原始流最终都在file
      • 2>&1 >file:

        • 2>&1 首先将 stderr 重定向到 then-original 标准输出;由于文件描述符2 不参与进一步的重定向,因此stderr 输出将转到当时定义为的任何stdout - 即原始 stdout,鉴于此是第一个重定向。
          • 从技术上讲,原来的 stdout 文件描述符是重复的,而那个重复的就是 stderr 所指的,这就解释了为什么它不受以后的 stdout 重定向的影响。
        • >file 然后将原始 stdout 重定向到 file - 但这对已经锁定的 stderr 重定向没有任何影响。
        • 最终结果是在file 中仅捕获直接发送到stdout 的输出,而将发送到stderr 的输出输出到(原始的、未重定向的)stdout。

      【讨论】:

        【解决方案3】:

        这个错误:

        ls: *.xyz: No such file or directory
        

        ls二进制写入stderr

        但是在这个命令中:

        ls -al *.xyz 2>&1 1> files.lst
        

        您首先将stderr 重定向到stdout,默认情况下转到tty(终端)

        然后您将stdout 重定向到文件files.lst,但是请记住stderr 不会重定向到文件,因为您将stderr 重定向到stdout 重定向之前 @987654332 @ 到 file 重定向。在这种情况下,您的 stderr 仍会被写入 tty

        但是在第二种情况下,您更改重定向的顺序(首先将stdout 更改为file,然后将stderr 更改为stdout)并且正确地将stderr 重定向到file,这也被stdout.

        【讨论】:

        • Stdout 默认不是 tty。它只是从外壳继承而来。如果 shell 是交互式的并在 tty 上运行,则 stdout 和 stderr 默认为该 tty。如果 shell 没有在 tty 上运行,那么它们不会。我意识到这似乎是一个傻瓜,但重要的是不要将标准输出与终端混为一谈,人们经常犯这个错误。
        • 是的,我的意思是在交互式 shell 的上下文中,因为 OP 在交互式 shell 中运行此命令。
        【解决方案4】:

        因为顺序很重要。 在第一种情况下,您首先将 stderr (2) 重定向到 stdout (1)。 然后将 (1) 重定向到一个文件。但是 stderr (2) 仍然指向运行命令的 shell 的标准输出。在这种情况下,将 (1) 指向一个文件不会更改 (2) 指向的输出设备,因此它仍会转到终端。

        在第二种情况下,您将 stdout (1) 重定向到文件。然后你将stderr(2)指向同一个地方1所指向的地方,也就是文件,所以错误信息转到文件中。

        【讨论】:

        • 大部分正确,但我质疑第 4 句的措辞“但 stderr 仍然指向 stdout” fd 1 重定向到文件后,fd 2 仍然与文件绑定(tty 是文件)与 shell 的标准输出相关联,但不是命令的标准输出。也许将那句话更改为“stderr 仍然指向运行命令的 shell 的标准输出”。更重要的是,不要将标准输出与终端混为一谈。它通常是一个终端,但(可能更经常)不是。
        • @WilliamPursell 同意,我已经进行了编辑。在这种特定情况下这可能是合理的,但不是一个好的通用答案。
        猜你喜欢
        • 1970-01-01
        • 2019-06-25
        • 1970-01-01
        • 1970-01-01
        • 2013-11-19
        • 1970-01-01
        • 2011-11-22
        相关资源
        最近更新 更多