【问题标题】:Redirection operator is not working as expected重定向运算符未按预期工作
【发布时间】:2020-11-25 06:30:23
【问题描述】:

我的当前目录中有 2 个文件:/home/tushar/Desktop

file1.txt
file4.txt

所以当我点击命令时:

1. ls file{1..4}.txt

输出:

ls: cannot access 'file2.txt': No such file or directory
ls: cannot access 'file3.txt': No such file or directory
file1.txt  file4.txt

现在我将 stderr 和 stdout 重定向到文件 res.txt 使用:

2. ls file{1..4}.txt 1>res.txt 2>res.txt

res.txt:

file1.txt
file4.txt
ile2.txt': No such file or directory
ls: cannot access 'file3.txt': No such file or directory

如上,我们可以看到文件res.txt中省略了stderr的部分内容。

现在我将命令更改为

3. ls file{1..4}.txt 1>res.txt 2>&1

res.txt:

ls: cannot access 'file2.txt': No such file or directory
ls: cannot access 'file3.txt': No such file or directory
file1.txt
file4.txt

上述两个命令完全相同,除了在第二个中我重定向了stderr,其中stdout使用文件描述符(&1)重定向。

现在我知道我应该使用 command附加模式 打开 res.txt,如下所示:

4. ls file{1..4}.txt 1>>res.txt 2>&1
OR
5. ls file{1..4}.txt &> res.txt

但我担心的是:

为什么cmd 2cmd 3的结果不同

为什么 cmd 3 即使不使用 >> 符号而不使用 cmd 2 也能得到正确的输出?

【问题讨论】:

  • 我假设你的意思是在最后一行 cmd 4 和 cmd 3,对吧?

标签: linux bash shell unix git-bash


【解决方案1】:

第二个问题:

因为2>&1 表示 fd 2 是 fd(文件描述符)1 的副本。然后 >>> 在追加或新/截断模式下打开 1(标准输入)。所以他们描述了两个不同的操作,使用相同的>符号,只是因为:1-他们正在处理文件描述符(>可以记住,而不是新符号,例如涉及$,2-这是一个无效的语法,在添加此类扩展之前,因此含义是唯一的。

问题 1 更棘手。您独立地打开同一个文件两次,并且程序独立地看到它们,因此它们可能具有不同的缓存和优先级。一般来说:不要做 2,但如果你这样做,你必须在每一行刷新输出(并保持行可能较短)。您可能会在日志记录中看到类似 2 的内容,但有时您还会看到两个程序的输出,它们混合并部分合并。

我也不确定 2. 是否已定义,是否在所有 POSIX 兼容的 shell 中可用。

【讨论】:

    【解决方案2】:

    在 cmd 3 中,您将 stderr 重定向到 res.txt。您将在 res.txt 中找到错误消息。

    【讨论】:

      【解决方案3】:

      为什么cmd 2和cmd 3的结果不一样?

      因为 stdout 和 stderr 都覆盖 res.txt 内容

      为什么 cmd 3 即使不使用 >> 符号但不使用 cmd 2 也能导出正确的输出?

      cmd 3 并不总是有效,请参阅“How to redirect and append both stdout and stderr to a file with Bash?

       ls file{1..4}.txt >>res.txt 2>&1
      

      这样会更好,因为文件是以追加模式打开的,而 stderr 的文件描述符 '2' 将使用相同的模式。

      【讨论】:

        【解决方案4】:

        更详细地回答第一个问题:当您运行 ls file{1..4}.txt 1>res.txt 2>res.txt 时,它会打开 res.txt 两次,独立。这意味着写入它的每个文件描述符对它在文件中的位置(它的偏移量或位置)都有不同的想法。

        当命令开始运行时,您有一个空文件,其中包含两个文件描述符,它们都准备好写入文件的开头。到目前为止一切顺利。

        ls 然后将其错误消息打印到标准输出,并将它们存储在文件中,从头开始。在这一点上我们还可以;文件如下所示:

        ls: cannot access 'file2.txt': No such file or directory
        ls: cannot access 'file3.txt': No such file or directory
        

        之后,ls 开始将找到的文件写入标准输出。这里我们有一个问题:stdout 文件描述符仍然指向文件的开头,所以它从头开始覆盖文件。它写入第一个文件名,给出:

        file1.txtt access 'file2.txt': No such file or directory
        ls: cannot access 'file3.txt': No such file or directory
        

        然后它写一个换行符,覆盖“cannot”中的“t”,给出:

        file1.txt
         access 'file2.txt': No such file or directory
        ls: cannot access 'file3.txt': No such file or directory
        

        然后它写入下一个文件名,给出:

        file1.txt
        file4.txtfile2.txt': No such file or directory
        ls: cannot access 'file3.txt': No such file or directory
        

        然后是最后一个换行符,这次是“file2”中的“f”:

        file1.txt
        file4.txt
        ile2.txt': No such file or directory
        ls: cannot access 'file3.txt': No such file or directory
        

        ...这就是你看到的最终结果。

        基本上,这两个描述符都试图同时修改同一个文件,但没有正确协调(如果一个是另一个的副本,它们会是这样),所以它们相互绊倒。

        【讨论】:

        • 我同意您在这里写的内容,但我缺少一些细微之处。您提到该文件已被覆盖,但我相信它是被覆盖的内部缓冲区。令我困惑的是为什么内部缓冲区对于两个重定向都是相同的。另外,我也相信用stdbuf -e0 -o0写的效果会不一样。
        • @Gordan Davidson。那为什么命令3的输出是正确的呢?
        • @tusharRawat 在命令 3 中,stdout 和 stderr 是同一个 FD 的副本,所以它们在文件中的位置是共享的,不能像这样不同步。
        猜你喜欢
        • 1970-01-01
        • 2015-05-11
        • 2019-06-20
        • 2021-04-13
        • 1970-01-01
        • 1970-01-01
        • 2022-01-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多