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

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

我从空目录开始。

$ touch aFile
$ ls
aFile

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

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

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

答案1

不,它并不像标准那样安全>>bar 2>&1

当你写作时

foo >>bar 2>>bar

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

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

现在,有一个问题:

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

您通常可以认为文件从两个不同的位置同时写入的概率非常低barfoo >>bar 2>&1但你>>bar 2>>bar却无缘无故地增加了十几个数量级。

[1] POSIX 行话中的“打开文件描述”。

答案2

当你这样做时会发生什么

some_command >>file 2>>file

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

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

使用

some_command >>file 2>&1

即使在 NFS 上也能工作。

然而,使用

some_command >file 2>file

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

例子:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

首先写入字符串hello(带有终止换行符),然后abc从标准错误写入后跟换行符的字符串,覆盖hell.结果是abc带有换行符的字符串,后跟第一个echo输出的剩余内容、一个o和换行符。

交换两个echo缠绕只会hello在输出文件中产生,因为该字符串是最后写入的并且比该abc字符串长。重定向发生的顺序并不重要。

使用更惯用的方式会更好、更安全

some_command >file 2>&1

答案3

这取决于您想要实现什么目标。由您决定是否可以在与输出相同的文件中出现错误。这只是使用 shell 的功能将文本保存在文件中,让您可以根据需要进行重定向。没有绝对的是或否。正如 Linux 中的一切都可以通过多种方式完成,这就是我ls notExistingFile existingFile >> output 2>&1 回答问题的方式:就重定向本身而言,是的,它是完全安全的。

相关内容