为什么将不同的流附加到文件是安全的?

为什么将不同的流附加到文件是安全的?

众所周知,将标准输出和错误重定向到同一文件cmd >out_err.txt 2>out_err.txt可能会导致数据丢失,如下例所示:

work:/tmp$ touch file.txt
work:/tmp$ ls another_file.txt
ls: cannot access 'another_file.txt': No such file or directory

以上是示例的设置代码。空文件file.txt存在并且another_file.txt不是一个东西。在下面的代码中,我天真地重定向到out_err.txt列出这些文件的输入和输出操作系统。

work:/tmp$ ls file.txt another_file.txt >out_err.txt 2>out_err.txt
work:/tmp$ cat out_err.txt 
file.txt
t access 'another_file.txt': No such file or directory

我们看到错误流中丢失了一些字符。然而,>>从复制示例的意义上来说,使用有效会保留整个输出和整个错误。

为什么以及如何cmd >>out_err.txt 2>>out_err.txt运作?

答案1

不确定它是否众所周知,但它的发生是因为这样做,两个文件句柄是完全独立的,并且具有独立的读/写位置。因此它们可以互相覆盖。 (它们对应于两个不同的打开文件描述,使用技术术语,遗憾的是,它很容易与术语“文件描述符”混淆。)

这只发生在foo > out.txt 2>out.txt,而不是foo > out.txt 2>&1,因为后者复制文件描述符(指相同的打开文件描述)。

追加时,全部写入转到文件的末尾,就像写入时一样。这是由操作系统以原子方式处理的,因此即使另一个进程也无法介入。因此,独立读/写位置的问题就得到了解决。 (除非它可能无法通过 NFS 工作,这是文件系统的限制。)

在您的示例中,错误消息ls: cannot access...首先写入文件的开头。 stderr fd 的写入位置现在位于文件末尾。然后 的常规输出file.txt<newline>也被写入,但是 stdout fd 的写入位置仍然在开始处,因此这 9 个字节覆盖了部分错误消息。

有了附加的 fd,无论发生什么,第二次写入都会结束。

答案2

简单重定向使用选项 O_CREAT 和 O_TRUNC 打开(2) 文件,这将创建一个空文件并将文件位置定位在第一个字节

附加文件使用 O_APPEND 选项打开它,这会导致在每次写入操作之前查找到文件的当前结尾。

man 2 open

    O_APPEND
          The file is opened in append mode.  Before  each  write(2),  the
          file  offset  is  positioned  at the end of the file, as if with
          lseek(2).  The modification of the file offset and the write op‐
          eration are performed as a single atomic step.

换句话说,内核保证追加不会发生冲突

相关内容