将stderr重定向到stdout还是将两者都重定向到同一个文件?

3

哪个更好?

  • cmd >>file 2>&1
  • cmd 1>>file 2>>file

它们之间有区别吗?


我知道选择第一个的两个原因:它也可以使用>代替>>。它更流行,因此熟悉shell脚本的人会立即理解。

但是,我仍然觉得第二种方式更易读,并且不需要了解[n]>&[n]语法,这对我来说有点困惑。


拥有两个打开的文件描述符(具有它们自己的,大多无意义的文件偏移量)算不算差异? - Davis Herring
通常您会将第二种形式表示为cmd 1>>outfile 2>>errfile(这是使用两个描述符的唯一好处) - David C. Rankin
在现代的shell中,你只需要使用&>>(bash)或>>&(tcsh)。 - Davis Herring
@DavisHerring 如果这是一个实际问题(速度/最大打开文件数/...),那么应该考虑。但我更想知道是否拥有两个文件描述符会产生真正的差异,例如混淆输出序列。 - Sam
@Sam:我的意思是,bash在4版本之前没有&>>(只有&>, >>, 和 >&),一些人仍然没有这个功能(参考链接:https://stackoverflow.com/q/30708549/8586227)。 - Davis Herring
显示剩余5条评论
1个回答

3

有什么区别?

让我们来看一下每个命令的含义。我将假设问题没有提出更具体的要求,因此适用POSIX shell规范

第一个命令是cmd >>file 2>&1。在设置指定的重定向后运行cmd

重定向>>file使用O_APPEND打开指定的文件。如open的规定所述,这创建了一个新的打开文件描述符,其中特别包含当前文件偏移量,并安排文件描述符 1引用该描述符。 O_APPEND的含义是“在每次写入之前,文件偏移量将设置为文件末尾”。

重定向2>&1表示文件描述符2“将变成文件描述符1的副本”。该规范有点模糊,但我认为唯一合理的解释(以及shell实际上的做法)是它意味着调用dup2(1, 2),它“将导致文件描述符[2]引用与文件描述符[1]相同的打开文件描述符”。至关重要的是,我们获得另一个文件描述符,但继续使用同一个文件描述符,这意味着它们都具有相同的文件偏移量。

第二个命令是cmd 1>>file 2>>file。基于上述引用的规范,这会为file创建两个单独的文件描述符,每个都具有自己的偏移量。

现在,如果cmd对文件描述符1和2唯一要做的事情就是调用write,那么这两种情况是等效的,因为每次对write的调用将在执行写操作之前原子地更新偏移量,因此第二个命令中存在两个独立的偏移量不会有任何可观察的影响。
然而,如果cmd执行其他操作,例如lseek,则这两种情况是不等价的,因为这将显示第一个命令具有一个共享偏移量,而第二个命令具有两个独立偏移量。
此外,上述内容假定了O_APPEND的POSIX规定语义。但是真实的计算机系统并不总是实现它;例如,NFS没有原子追加。在没有原子追加的情况下,即使只进行write,第二个命令也可能表现出不同的行为(最有可能损坏输出)。
哪种更好?
由于这两个命令的含义不同,因此哪个更好很可能取决于你的意图。我猜测,在几乎所有情况下,意图是将cmd的标准输出和标准错误附加到file中,假定cmd只向这些描述符中写入内容。这正是第一个命令的含义(cmd >>file 2>&1),因此是更好的选择。
尽管第二个命令使用的shell特性更少,因此对于某些人来说可能更容易理解,但对于那些熟悉重定向语法的人来说,它可能看起来很奇怪,并且在某些情况下甚至可能表现出与预期不同的行为。因此,我建议不要使用第二个命令,并且如果我在维护某些代码时发现它,我会倾向于将其更改为第一种形式。
当然,如果您真的想要单独的文件描述符,从而获得单独的文件偏移量,则第二个命令是有意义的,只要附近放置一条注释,解释不寻常的结构的基本原理即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接