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
我还尝试了 dash
和 ksh
- 同样的事情。
问题是:
Is this a bug or the right way?
这是正确的方法。
/proc/$$/fd/1
是 testfile
的符号链接,因此 >> /proc/$$/fd/1
与 >> testfile
相同。它是一个单独的子进程,写入 testfile
。当这个子进程终止时,您会看到它在文件中写入的内容。它对您的父进程没有影响。
STDOUT_FILENO
并不特别,它就像任何其他文件描述符一样。
为什么子进程会影响您的父进程文件描述符位置?因为dup
licated file descriptors refer to the same open file description。读两遍——“文件描述符”!=“文件描述”。它就像一个 double-mapping。我们说,文件 descriptors 没有被复制,它们是 duplicated,它们 share 文件偏移量和文件状态标志,因为它们共享文件描述tion。参见 man 2 dup
和 man 2 open
。
当一个单独的进程打开同一个文件时,它有一个单独的文件描述,所以它不会影响你的进程文件描述。
(它的名字太糟糕了。我在心理上将“文件描述符”称为“系统句柄”(因为它可以引用 任何 内核想要的东西)并调用“文件描述”就像“文件数据”(如果它是一个文件,它可以是内核想要的任何东西)。然后很清楚 - 这个“系统句柄”指的是这个“文件数据”,“系统句柄”在进程之间重复,这意味着两个“系统句柄”指的是同一个打开的“文件数据”。“文件数据”存储文件位置和文件锁等。进程有“句柄”,而不是“数据”。“文件数据”会自动删除,当最后一次“系统句柄”正在被删除(令人困惑的 man 2 unlink
)。)
我有一个最小的完整示例,其中 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
我还尝试了 dash
和 ksh
- 同样的事情。
问题是:
Is this a bug or the right way?
这是正确的方法。
/proc/$$/fd/1
是 testfile
的符号链接,因此 >> /proc/$$/fd/1
与 >> testfile
相同。它是一个单独的子进程,写入 testfile
。当这个子进程终止时,您会看到它在文件中写入的内容。它对您的父进程没有影响。
STDOUT_FILENO
并不特别,它就像任何其他文件描述符一样。
为什么子进程会影响您的父进程文件描述符位置?因为dup
licated file descriptors refer to the same open file description。读两遍——“文件描述符”!=“文件描述”。它就像一个 double-mapping。我们说,文件 descriptors 没有被复制,它们是 duplicated,它们 share 文件偏移量和文件状态标志,因为它们共享文件描述tion。参见 man 2 dup
和 man 2 open
。
当一个单独的进程打开同一个文件时,它有一个单独的文件描述,所以它不会影响你的进程文件描述。
(它的名字太糟糕了。我在心理上将“文件描述符”称为“系统句柄”(因为它可以引用 任何 内核想要的东西)并调用“文件描述”就像“文件数据”(如果它是一个文件,它可以是内核想要的任何东西)。然后很清楚 - 这个“系统句柄”指的是这个“文件数据”,“系统句柄”在进程之间重复,这意味着两个“系统句柄”指的是同一个打开的“文件数据”。“文件数据”存储文件位置和文件锁等。进程有“句柄”,而不是“数据”。“文件数据”会自动删除,当最后一次“系统句柄”正在被删除(令人困惑的 man 2 unlink
)。)