我编写了一个程序,创建一个空文本文件,如果成功,则打印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 %dn", fd);
printf("Succeedn");
}
使用./a.out
运行时效果良好
当使用./a.out >&-
运行时,open()
返回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 %dn", final_fd);
printf("Standard outputn");
}
它工作得很好,printf
会失败并返回-1,但我不在乎。
我知道我可以通过检查open()
返回值是否大于或等于3来进行优化,但这只是一个例子。
使用./a.out >/dev/null
是有效的,但不是一个解决方案,我不能禁止我的程序的用户关闭标准输出。
这是处理这个问题的正确方法吗?
在POSIX上,open
将始终返回最小的可用文件描述符。处理此问题的唯一方法是在程序开始时检查文件描述符0。。。2例如使用isatty
-如果isatty
返回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在程序启动时,三个文本流是预定义的,不需要显式打开——标准输入(用于读取常规输入)、标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。在最初打开时,标准错误流没有被完全缓冲;当且仅当可以确定流不涉及交互式设备时,标准输入流和标准输出流被完全缓冲。
这可能被认为是C启动例程中的失败——在我看来,它应该至少向/dev/null
打开这些流
尽管现在我尝试了这一点,但我已经得出结论,在关闭标准流的情况下运行程序不仅会大脑受损,而且会完全脑死亡,因为任何fopen
也会在stdin
、stdout
或stderr
上打开,所以我会将代码修改为:
struct stat statbuf;
for (int fd = 0; fd <= 2; fd++) {
if (fstat(fd) == 0 && errno == EBADF) {
fprintf(stderr, "Id10t error: closed standard IO descriptor %dn", fd);
abort();
}
}