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

Swe*_*ine 13 io-redirection streams

众所周知,将标准输出和错误重定向到同一文件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
Run Code Online (Sandbox Code Playgroud)

以上是示例的设置代码。空文件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
Run Code Online (Sandbox Code Playgroud)

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

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

ilk*_*chu 23

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

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

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

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

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

  • 有关文件位置的更多信息可以在 lseek(2) 手册页中找到 (2认同)
  • 小问题:并非所有写入都是原子的,内核仅保证一定大小的原子性(我忘记了它是哪个常量)。对于许多写入来说,这并不重要,但对于大量写入,您仍然会看到竞争 (2认同)

Jas*_*sen 6

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

\n

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

\n

man 2 open

\n
    O_APPEND\n          The file is opened in append mode.  Before  each  write(2),  the\n          file  offset  is  positioned  at the end of the file, as if with\n          lseek(2).  The modification of the file offset and the write op\xe2\x80\x90\n          eration are performed as a single atomic step.\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n