如何处理 open() 返回 1

How to deal with open() returning 1

我编写了一个创建空文本文件的程序,如果成功则打印 Succeed

cc main.c

编译
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

int main()
{
    int fd;

    // Create empty text file
    fd = open("foo.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    assert(fd != -1);

    fprintf(stderr, "File descriptor is %d\n", fd);

    printf("Succeed\n");
}

当 运行 与 ./a.out

时效果很好

when 运行 with ./a.out >&-, open() returns 1, 这个我理解。

但是,printf 正在写入我的文件!

$ cat foo.txt
Succeed

我不想要这个,所以我写了以下内容:

int main()
{
    int fd1;
    int fd2;
    int fd3;
    int fd4;

    fd1 = open("foo.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    assert(fd1 != -1);

    fd2 = open("foo.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    assert(fd2 != -1);

    fd3 = open("foo.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    assert(fd3 != -1);

    fd4 = open("foo.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    assert(fd4 != -1);

    int final_fd = fd4;
    close(fd1);
    close(fd2);
    close(fd3);

    fprintf(stderr, "File descriptor is %d\n", final_fd);

    printf("Standard output\n");
}

它运行良好,printf 会失败并且 return -1 但我不在乎。

我知道我可以通过检查 open() return 值是否大于或等于 3 来优化,但这只是一个例子。

使用 ./a.out >/dev/null 可行但不是解决方案,我不能禁止我的程序的用户关闭标准输出。

这是处理问题的正确方法吗?

在 POSIX open 总是 return 最小的可用文件描述符。处理这个问题的唯一方法是在程序的开头检查文件描述符 0 ... 2 说 isatty - if isatty returns 0 并且 errno 设置为 EBADF,文件描述符未被使用,然后应该从 /dev/null:

打开一个新的描述符
for (int fd = 0; fd <= 2; fd++) {
    errno = 0;
    if (! isatty(fd) && errno == EBADF) {
        open("/dev/null", O_RDWR);
    }
}

另请注意,那些未打开的流的状态根本不符合标准。 C11 7.21.3p7:

7 At program startup, three text streams are predefined and need not be opened explicitly -- standard input (for reading conventional input), standard output (for writing conventional output), and standard error (for writing diagnostic output). As initially opened, the standard error stream is not fully buffered; the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device.

这可以被认为是 C 启动例程中的一个失败 - 在我看来,它应该至少 /dev/null 打开这些流


虽然现在我尝试了这个,但我得出的结论是 运行 关闭标准流的程序不仅脑损伤而且完全脑死亡,因为任何 fopen 也会打开 stdinstdoutstderr,所以我将代码修改为:

struct stat statbuf;
for (int fd = 0; fd <= 2; fd++) {
    if (fstat(fd) == 0 && errno == EBADF) {
        fprintf(stderr, "Id10t error: closed standard IO descriptor %d\n", fd);
        abort();
    }
}