为什么 1>a.txt 2>&1 与 1>a.txt 2>a.txt 不同?(示例显示)

aka*_*kai 19 shell

编辑

请不仅查看已接受的答案,还请查看其他答案。

为什么将 STDOUT 和 STDERR 重定向到同一个文件不起作用,尽管它看起来与 1>[FILENAME] 2>&1 相同?

下面是一个例子:

perl -e 'print "1\n" ; warn "2\n";' 1>a.txt 2>a.txt
cat a.txt
# outputs '1' only.
Run Code Online (Sandbox Code Playgroud)

嗯,为什么?我认为这是有效的,因为...... STDOUT 被重定向到 a.txt,STDERR 也是如此。STDERR 怎么了?

use*_*686 62

使用1>a.txt 2>&1,文件描述符#1 被复制到#2。它们都引用相同的“打开文件”,并且共享当前位置和 r/w 模式。(实际上使用 2>&1 和 2<&1 之间根本没有区别。)

使用1>a.txt 2>a.txt,两个文件描述符都是独立打开的,并且具有单独的光标位置。(文件也被截断了两次。)如果你向 fd #1 写入“Hello”,它的位置会前进到字节 5,但 fd #2 保持在字节 0。打印到 fd #2 只会覆盖从 0 开始的数据.

这很容易看出第二次写入是否较短:

$ perl -e 'STDOUT->print("abcdefg\n"); STDOUT->flush; STDERR->print("123");' >a.txt 2>a.txt

$ cat a.txt 
123defg
Run Code Online (Sandbox Code Playgroud)

请注意,Perl 具有内部缓冲,因此在此示例中,需要显式 flush() 以确保在 fd #2 数据之前写入 fd #1 数据。否则,流将在退出时以不可预测的顺序刷新。

为了比较,如果文件描述符是共享的,则写入只是相互跟随:

$ perl -e 'STDOUT->print("abcdefg\n"); STDOUT->flush; STDERR->print("123");' >a.txt 2>&1

$ cat a.txt 
abcdefg
123
Run Code Online (Sandbox Code Playgroud)

  • @JonasSchäfer,这就是 `&gt;&gt;` 的用途。 (11认同)
  • @MarkStewart 我不明白你的第二个问题。关于第一个,我相信它是一般的。`&gt;&amp;` 和 `&lt;&amp;` 之间的唯一区别似乎是左文件描述符的默认值。如果它在给出的示例中指定,那么应该没有区别。它们都会导致调用 `dup2(1, 2)`。 (3认同)
  • @MarkStewart 啊,[当然](https://unix.stackexchange.com/questions/37660/order-of-redirections/37662#37662)。 (3认同)
  • @JonasSchäfer:显然 [O_APPEND 尚不存在](https://unix.stackexchange.com/a/202827/2168) 最初编写 sh 时,`&gt;` 的行为现在或多或少是一成不变的任何想要保持 sh 兼容的 shell。(使用它会使 seek() 无法返回,这无济于事,这是行为的另一个变化。) (2认同)

Eug*_*eck -2

您的两个重定向都会截断文件,因此第二个(按执行时间顺序)将覆盖第一个。尝试

rm a.txt ; touch a.txt ; perl -e 'print "1\n" ; warn "2\n";' 1>>a.txt 2>>a.txt
Run Code Online (Sandbox Code Playgroud)

或者只是使用相同的文件描述符

perl -e 'print "1\n" ; warn "2\n";' 1>a.txt 2>&1
Run Code Online (Sandbox Code Playgroud)

  • 这是不对的。该文件确实被截断了两次,但两次都是在 perl 启动之前完成的。 `&gt;` 的截断行为在这里无关紧要。之所以会覆盖另一个,是因为它们具有独立的光标位置。在追加模式下这不是问题的原因是每次写入都会在写入之前自动查找到末尾。在执行顺序上,我们看到第一个覆盖了第二个。 Perl 在写入文件时缓冲 stdout,但 stderr 保持未缓冲状态。因此,“1”实际上是在 perl 即将终止时、在警告输出之后写入的。 (22认同)
  • 您在评论中写道:*关于截断的要点是文件指针随后保持为零*。是的,作为追加重定向的一部分,每次写入时的隐式搜索结束是这里的关键。我对你的*答案*投了反对票,因为你的答案中没有提出这个关键点。相反,您对截断顺序的重要性提出了一些奇怪的主张。但是 `2&gt;a.txt 1&gt;a.txt` 的行为方式相同,因此实际上截断的时间顺序并不重要,而是第二次写入时发生的情况。请记住,两次截断都发生在第一次写入之前、程序运行之前。 (5认同)
  • 追加重定向的工作原理不同:因为使用“O_APPEND”打开使写入始终追加到文件的当前末尾,尊重其他写入(通过其他进程,或通过此进程在其他未“复制”的文件描述符上)并且不共享相同的文件位置)。 “&gt;&gt;”与“&gt;”在“O_APPEND”和“O_TRUNC”中都是不同的,所以是的,非截断重定向有效,但不完全是*因为*它不会截断。 (3认同)
  • 我已经尽力向你解释这一点了。令我惊讶的是,我什至向您提供了代码,以便您可以看到发生了什么并进行回复。 (3认同)