3

我想fprintf通过将目标 FILE 重定向到/dev/null. 但我可以肯定的是,fopen("/dev/null", "w");永远不会回来NULL。换句话说,是否每次都可以打开这个“文件”?

如果是这样,我可以使用这个不错的三元运算符:

FILE *whereToPrint = (strcmp(custom_topic, ERROR_TOPIC) == 0) ? fopen("/dev/null", "w") : stdout;

fprintf(whereToPrint, "Message sent!\n\n");
4

4 回答 4

8

是的,在一个正常运行的系统上,/dev/null是世界可写的:

 ls -l /dev/null
 crw-rw-rw- 1 root root 1, 3 Jul 20  2017 /dev/null

所以它会一直有效。这不是抑制输出的最有效方法。如果你不想写,最好不要尝试写。但如果输出不多,那没关系。

有人指出,可以root设置 的权限/dev/null,使其不可写other。或者他们可以完全删除该设备。这是真的.. 但这会导致 unix 损坏。 /dev/null应该有我上面显示的权限..它是这样安装的,永远不应该改变。fopen()不过,您应该检查或open()在打开任何文件时的返回值。

于 2018-02-23T19:29:56.297 回答
5

是的,有可能由于某种原因而fopen无法打开/dev/null,例如权限问题/dev/null。虽然非常罕见,但不能否认这种可能性。

于 2018-02-23T19:38:49.490 回答
4

可能无法写入/dev/null或丢失。我的意思是,您可以打开一个 root shell 并键入rm /dev/null并按 RETURN,它会愉快地继续删除设备节点。

如果程序/dev/null不可写或丢失,则程序失败是合理的。许多其他程序具有该属性。但是你的程序盲目地假设成功是不合理的,除非它是一个一次性的测试程序,除了你自己之外没有人会运行。写另外两行来调用和if is 。老实说,即使它只有我会运行的一次性测试程序,我也会包括在内。打字不超过十秒,谁知道呢?也许我遇到的问题是一些错误的系统脚本在我背后删除了 /dev/null !fopen("/dev/null", "w")perrorexitwhereToPrintNULL

编辑:我突然想到你可能会犹豫检查结果fopen不是因为额外的输入,而是因为你不想弄乱你的“漂亮的三元表达式”。没有必要把它搞砸;您只需在之后立即进行检查:

FILE *whereToPrint = (strcmp(custom_topic, ERROR_TOPIC) == 0) ? fopen("/dev/null", "w") : stdout;
if (!whereToPrint) {
    perror("/dev/null");
    exit(1);
}

如果whereToPrint已设置为 ,则不等于stdoutNULL并且检查将成功。

如果你有一大堆这些错误主题,那么你应该把它们和它们的名字放在一个表中,这样你就可以在初始化时循环它们,而且不需要/dev/null多次打开:

enum error_topic_codes { 
    ET_INPUT, ET_CRUNCHING, ET_FROBNICATING, ET_OUTPUT,
    ET_MISC
};
struct error_report_s {
    const char *label;
    FILE *fp;
};
static struct error_report_s error_destinations[] = {
    /* ET_INPUT */        { "input", 0 },
    /* ET_CRUNCHING */    { "crunching", 0 },
    /* ET_FROBNICATING */ { "frobnicating", 0 },
    /* ET_OUTPUT */       { "output", 0 },
    /* ET_MISC */         { "misc", 0 },
};

void error_report_init (const char *squelched)
{
    FILE *devnull = fopen("/dev/null", "w");
    if (!devnull) {
        perror("/dev/null");
        exit(1);
    }
    for (int i = 0; i <= ET_MISC; i++)
        error_destinations[i].fp =
            strstr(squelched, error_destinations[i].label)
            ? devnull : stderr;
    /* FIXME 2018-02-23: This leaks a FILE if none of the
       error categories are squelched.  */
}
于 2018-02-23T19:42:49.897 回答
3

是的,/dev/null总是可以打开的——除非它不是。

这听起来很傻,但我不是在开玩笑。如果/dev/null无法打开,您可能有一个严重损坏的、可能处于临界状态的非功能系统——但知道这并不等同于保证文件是可打开的。

打开文件失败总是有原因的。你永远不应该找借口不检查fopen失败的返回值。

它可能永远不会发生,你知道,它可能永远不会发生在正常运行的系统上,但问问你自己,如果/dev/null“不可能”打开失败会发生什么?

  1. 如果您的程序检查fopen失败,它将打印一条消息,如"Impossible error! Can't open /dev/null",并且很清楚发生了什么。

  2. 如果你的程序没有检查fopen失败,它会在第一次尝试打印某些东西时神秘地崩溃whereToPrint,你的用户会想知道哪里出了问题。

神秘地崩溃的程序是坏的。告诉你发生了什么的程序很好。

而且,您可以告诉用户发生的事情越多越好。我建议 print "Impossible error! Can't open /dev/null",这总比没有好,但它实际上仍然很不完整。您真的应该编写行为如下的代码:

#include <stdio,h>
#include <string.h>
#include <errno.h>

FILE *whereToPrint;

if(strcmp(custom_topic, ERROR_TOPIC) != 0)
    whereToPrint  = stdout;
else if((whereToPrint = fopen("/dev/null", "w")) == NULL) {
    fprintf(stderr, "Impossible error! Can't open /dev/null: %s\n", strerror(errno));
    exit(1);
}

现在,在它失败的“不可能”场合,它会告诉你为什么它无法打开/dev/null,这可能是非常有用的信息。它可能会打印

Impossible error! Can't open /dev/null: No such file or directory

如果/dev/null不知何故不存在。或者它可能会打印

Impossible error! Can't open /dev/null: Permission denied

如果像其他人建议的那样,有人错误地限制了/dev/null您系统上的权限。或者它可能会打印

Impossible error! Can't open /dev/null: Too many open files

事实上即使在正确配置的系统上,由于程序中的错误,它也可能会失败!

例如,回到你的“漂亮的三元运算符”,如果你曾经写过类似的东西

void log_message(const char *msg)
{
    FILE *whereToPrint = (strcmp(custom_topic, ERROR_TOPIC) == 0) ?
                                       fopen("/dev/null", "w") : stdout;
    fprintf(whereToPrint, "%s", msg);
}

您迟早很可能会收到“打开的文件过多”错误,因为log_message()我在这里编写的函数当然有一个错误:它每次调用时都会打开文件(可能),但从不关闭它。

三元运算符的“不错”用法——或任何其他“不错”的技巧——写起来很有趣,如果它们有效,它们很好,但请不要以牺牲其他为代价来坚持它们,代码中更重要的方面,例如确保它在所有情况下都能正常工作。:-)

于 2018-02-23T21:05:12.483 回答