在没有文件描述符副本的情况下将 stdout 和 stderr 重定向到同一个文件是否安全?

exi*_*tus 27 io-redirection stdout stderr

我从空目录开始。

$ touch aFile
$ ls
aFile
Run Code Online (Sandbox Code Playgroud)

然后我有ls两个参数,其中一个不在此目录中。我将两个输出流都重定向到一个名为output. 我使用>>是为了避免同时写入。

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile
Run Code Online (Sandbox Code Playgroud)

这似乎有效。这种方法有什么危险吗?

Kus*_*nda 22

当你这样做时会发生什么

some_command >>file 2>>file
Run Code Online (Sandbox Code Playgroud)

file将打开以追加两次。在 POSIX 文件系统上这样做是安全的。无论数据是通过标准输出流还是标准错误流,在打开文件进行追加时发生的任何写入都将发生在文件末尾。

这依赖于对底层文件系统中的原子追加写入操作的支持。某些文件系统,例如 NFS,不支持原子追加。例如,参见StackOverflow 上的问题“在 UNIX 中文件追加是原子的吗?”

使用

some_command >>file 2>&1
Run Code Online (Sandbox Code Playgroud)

即使在 NFS 上也能工作。

但是,使用

some_command >file 2>file
Run Code Online (Sandbox Code Playgroud)

不安全,因为 shell 会截断输出文件(两次),并且在任一流上发生的任何写入都将覆盖另一个流已经写入的数据。

例子:

some_command >>file 2>>file
Run Code Online (Sandbox Code Playgroud)

所述hello字符串被首先写入(与终止行),然后将串abc跟着一个新行从标准误差写入,覆盖hell。结果是abc带有换行符的字符串,后跟第一个echo输出的剩余部分、ano和换行符。

交换两个echo围绕伤口仅hello在输出文件中产生,因为该字符串最后写入并且比abc字符串长。重定向发生的顺序无关紧要。

使用更惯用的词会更好更安全

some_command >file 2>&1
Run Code Online (Sandbox Code Playgroud)


mos*_*svy 22

不,它不仅与标准一样安全>>bar 2>&1

当你写作

foo >>bar 2>>bar
Run Code Online (Sandbox Code Playgroud)

您使用bar两次打开文件O_APPEND,创建两个完全独立的文件对象[1],每个文件对象都有自己的状态(指针、打开模式等)。

这与2>&1仅调用dup(2)系统调用非常不同,并且使 stderr 和 stdout 为同一文件对象具有可互换的别名。

现在,有一个问题:

O_APPEND如果多个进程同时将数据附加到一个文件,则可能会导致 NFS 文件系统上的文件损坏。这是因为 NFS 不支持附加到文件,所以客户端内核必须模拟它,这在没有竞争条件的情况下是无法完成的。

通常你可以对文件的概率计算类似barfoo >>bar 2>&1从两个不同的地方是相当低的,同时被写入。但是,>>bar 2>>bar你只是将它增加了十几个数量级,没有任何理由。

[1] POSIX 术语中的“打开文件描述”。

  • 形式上,对于附加模式文件,它**是安全的**。引用的问题是 NFS 中的一个 **bug**,使其不适合(不符合 POSIX 标准)作为文件系统。但是,对于非附加模式的情况,它永远不会安全。 (3认同)