为什么调用dup2会出错?

Why does the calling of dup2 go wrong?

如你所见,程序有两个文件指针sport和fruit指向文件fruit.txt。问题是运行程序后,sport.txt为空,fruit.txt包含汉字。我预计 sport.txt 应该包含单词 "basketball" 因为它是在重定向发生之前写入文件的。那么,这里有什么问题?

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "../cus_header/cus_header.h"

int main(){

    FILE *fruit = fopen("fruit.txt", "w");
    if(!fruit)
        error("cannot open fruit.txt");

    FILE *sport = fopen("sport.txt", "w");
    if(!sport)
        error("cannot open sport.txt");

    int de_sport = fileno(sport);
    int de_fruit = fileno(fruit);

    printf("file number of sport.txt: %i and of fruit.txt: %i\n", de_sport, de_fruit);

    fwrite("basketball", sizeof(char), 10, sport);

    fwrite("apple", sizeof(char), 6, fruit);

    if(dup2(de_fruit, de_sport) == -1)
        error("cannot redirect");

    fwrite("basketball", sizeof(char), 10, sport); //???
    fwrite("apple", sizeof(char), 6, fruit); // ???

    fclose(sport);
    fclose(fruit);

    return 0;
}

正如评论已经提到的,您不应该将文件操作与流(使用 FILE*fopenfwritefclose)与原始文件操作混合(使用文件描述符,openwriteclosedup2)。尤其不要像您在这段代码中那样将它们混合在同一个文件中 pointer/descriptor。

让我们浏览一下代码,看看它为什么会这样:

FILE *fruit = fopen("fruit.txt", "w");
...
FILE *sport = fopen("sport.txt", "w");

你不应该关心 FILE 结构的样子,我们假设它把底层文件描述符保存在某处。

int de_sport = fileno(sport);
int de_fruit = fileno(fruit);

您创建的局部变量包含与两个 FILE* 引用相同的文件描述符。

fwrite("basketball", sizeof(char), 10, sport);
fwrite("apple", sizeof(char), 6, fruit);

你在两个文件中分别写一些东西。因为 C 文件流在默认情况下是缓冲的,所以磁盘上文件的实际写入可能不会立即发生(在您的情况下不会)。

dup2(de_fruit, de_sport)

这将关闭文件描述符 de_sport 并使其引用与 de_fruit 相同的文件。实际数值保持不变,只是它们引用的实际文件发生了变化。这意味着两个 FILE 句柄将在 dup2 调用后写入同一个文件。

fwrite("basketball", sizeof(char), 10, sport); //???
fwrite("apple", sizeof(char), 6, fruit); // ???

这将写入同一个底层文件,因为这两个描述符现在引用同一个文件。但是同样,因为流是缓冲的,这实际上可能只是附加到那两个 FILE* 的缓冲区。

fclose(sport);
fclose(fruit);

这会刷新缓冲区,因此实际写入磁盘的操作发生在此处。因为描述符已更改,如果直到现在都没有刷新,两个流实际上将刷新到磁盘上的同一个文件。

这可能就是您看到该行为的原因,但请记住,您所做的并不安全,行为或文件内容可能有所不同。