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


27

我从空目录开始。

$ 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

这似乎可行。这种方法有危险吗?


6
那是一次快速的否决。花了大约五秒钟。您能告诉我如何快速评估我的问题的价值吗?而且更好的是,它有什么问题以便我可以改进它?
exit_status

您为什么不在这里使用更多标准ls aFile not_exist &>>output?(请注意,我假设您正在使用bash。)
FedonKadifeli

5
因为那并不能帮助我理解我要问的问题。我知道如何将这些流重定向到同一文件,甚至是可移植的。我想知道的是,我在问题中提出的建议是否有问题。@FedonKadifeli
exit_status

1
@FedonKadifeli &>>不是标准的。这是一种已弃用的模棱两可的语法,在不同的shell中工作方式有所不同。我想知道你们从哪里得到东西。
比利叔叔

4
Bash不是标准。POSIX标准要求ls &>>foo ...应将其解析为两个comands ls &>>foo ...,这是/bin/shUbuntu之类的其他shell 解析它的方式。对于不赞成使用的内容,您可以在这里查看 -尽管我不认为这是任何一种授权。但是,您可能会问bash维护者他们是否考虑使用该好主意。
比利叔叔

Answers:


22

不,它不像标准产品那样安全>>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语言中的“打开文件描述”。


3
正式地,对于追加模式文件,它是安全的。引用的问题是NFS中的错误,使其不适合(不符合POSIX)作为文件系统。但是,对于非附加模式情况,它永远都不安全。
R ..

1
没关系。该OP的双追加是不是安全使用(除了是完全没有意义的)。O_APPEND无论如何,这都是一个错误-正确实施很麻烦。
mosvy,

我相信NFS竞争条件仅在不同客户端之间存在。客户端操作系统应协调其进程之间的所有写入。
巴尔马尔

如果客户端操作系统只关心自己对nfs文件的视图,那么@Barmar就是正确的。但是,当写入以开头的nfs文件时O_APPEND,客户端将首先从服务器检索文件的“实际”大小(“重新验证” inode),然后执行seek + write + cacheed inode更新,只有最后一部分是在锁下完成,这意味着第一部分仍然可以从服务器检索过时的大小,并从本地/缓存的inode覆盖正确的大小。同样的问题lseek(SEEK_END)
mosvy

我仍然看不到这怎么可能导致同一客户端上两个流之间的竞争状况。两个流都应引用相同的本地缓存的索引节点。
巴尔马尔

22

当你做的时候会发生什么

some_command >>file 2>>file

是,file将用于追加两次被打开。在POSIX文件系统上这样做是安全的。打开文件进行追加时对文件的任何写操作都将发生在文件的末尾,而不管数据是来自标准输出流还是来自标准错误流。

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

使用

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和换行符。

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

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

some_command >file 2>&1

1
尽管现代shell确实如此,但在Bourne或Thomson shell(从何>>而来)中却不是这种情况,在那里>>可以进行编写并寻求结尾(我想是因为那时还没有发明O_APPEND)。即使在Solaris 10上,也可以/bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'输出b
斯特凡Chazelas

@StéphaneChazelasSolaris 10的实现sh或文件系统是否有问题?
库萨兰达

1
>>是最初的操作,没有使用O_APPEND打开,没有并且一直尝试结束。这不是什么大问题,而是它正在做的事情,并已记录在案。
斯特凡Chazelas

0

这取决于您要实现的目标。由您决定在与输出相同的文件中有错误是否可以。这只是将文本保存在具有外壳功能的文件中,您可以根据需要进行重定向。没有绝对的是或否。由于Linux中的所有操作都可以通过几种方式完成,因此这是我ls notExistingFile existingFile >> output 2>&1 要回答的问题:就重定向本身而言,是的,它绝对安全。


不仅仅是您在这里说的。使用>代替的相同练习>>将覆盖某些字符。因此,不仅仅是shell允许我重定向,因为当我使用重定向时>,结果会有所不同。因此,有细微差别>,还有什么用>>
exit_status

是的,情况会有所不同。如我所说,这取决于您的目标 >覆盖。>>-附加
天使
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.