Shell 重定向不会移动标准输出的文件位置

Shell redirection doesn't move file position for stdout

我有一个最小的完整示例,其中 Linux API 行为似乎有点模棱两可。标准输出已关闭,重新打开并用作常规文件的描述符:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
     system("echo > testfile");
     fclose(stdout);
     int fd = open("testfile", O_WRONLY);
     if (fd < 0) {
          perror("open():");
          return -1;
     }

     dprintf(fd, "Test\n");
     system("echo Test2");
     system("echo TestFFFFFFFFFFFF >> /proc/$$/fd/1"); // problem line
     sleep(5);
     dprintf(fd, "Test4\n");
     system("echo Test5");

     return 0;
}

睡前在testfile我看到

Test
Test2
TestFFFFFFFFFFFF

但睡眠后这一行被覆盖:

Test
Test2
Test4
Test5
FFFF

对于普通文件来说,这种行为似乎很奇怪:如果我们向文件写入一些东西——我们会以适当的方式改变它的位置(到最后)。
但是对于 stdout 它看起来很合理:如果我们写入 stdout - 我们在完成写入(到开始)后将其位置倒回。
这是错误还是正确的方法?

P.S。除了 bash 我还尝试了 dashksh - 同样的事情。

问题是:

Is this a bug or the right way?

这是正确的方法。


/proc/$$/fd/1testfile 的符号链接,因此 >> /proc/$$/fd/1>> testfile 相同。它是一个单独的子进程,写入 testfile。当这个子进程终止时,您会看到它在文件中写入的内容。它对您的父进程没有影响。

STDOUT_FILENO 并不特别,它就像任何其他文件描述符一样。

为什么子进程会影响您的父进程文件描述符位置?因为duplicated file descriptors refer to the same open file description。读两遍——“文件描述符”!=“文件描述”。它就像一个 double-mapping。我们说,文件 descriptors 没有被复制,它们是 duplicated,它们 share 文件偏移量和文件状态标志,因为它们共享文件描述tion。参见 man 2 dupman 2 open

当一个单独的进程打开同一个文件时,它有一个单独的文件描述,所以它不会影响你的进程文件描述。

(它的名字太糟糕了。我在心理上将“文件描述符”称为“系统句柄”(因为它可以引用 任何 内核想要的东西)并调用“文件描述”就像“文件数据”(如果它是一个文件,它可以是内核想要的任何东西)。然后很清楚 - 这个“系统句柄”指的是这个“文件数据”,“系统句柄”在进程之间重复,这意味着两个“系统句柄”指的是同一个打开的“文件数据”。“文件数据”存储文件位置和文件锁等。进程有“句柄”,而不是“数据”。“文件数据”会自动删除,当最后一次“系统句柄”正在被删除(令人困惑的 man 2 unlink)。)