【问题标题】:Is /dev/null always openable?/dev/null 总是可以打开的吗?
【发布时间】:2018-02-23 19:23:50
【问题描述】:

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

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

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

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

【问题讨论】:

  • 打开文件 -- any 文件 -- 可能失败的另一个原因是您的程序打开了太多文件。

标签: c dev-null


【解决方案1】:

是的,在正常运行的系统上,/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() 的返回值。

【讨论】:

  • 我不认为这是对“/dev/null 总是可打开吗?”问题的答案。你可以sudo chmod 000 /dev/null,虽然有点不正常。
  • 嗯,我认为这个答案非常好,我改变了主意,改用额外的 IF 语句,不输出不必要的文本!
  • @liliscent True.. 你也可以sudo chmod 000 /etc/passwdsudo rm -f /bin/bash .. 或者以root身份做无数的事情来破坏你的系统。我的答案是针对一个正常运行的系统......理想情况下,人们会想像往常一样检查 fopen() 的返回值......但我不想逃避基本问题。
  • 我将其添加到我的答案中
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

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

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

    如果whereToPrint已经设置为等于stdout,那么就不是NULL了,检查就会成功。

    如果你有一整个一堆这些错误主题,那么你应该把它们和它们的名字放在一个表中,这样你就可以在初始化时循环它们,也不需要打开@ 987654334@不止一次:

    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.  */
    }
    

    【讨论】:

      【解决方案4】:

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

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

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

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

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

      2. 如果您的程序未能检查 fopen 故障,它会在第一次尝试将内容打印到 whereToPrint 时神秘地崩溃,您的用户会想知道出了什么问题。

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

      而且,您越能告诉用户正在发生的事情越好。我建议打印"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() 函数当然有一个错误:它每次都打开文件(可能)它被调用,但从不关闭它。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-02
        • 1970-01-01
        • 1970-01-01
        • 2011-02-13
        • 2010-09-23
        • 2018-01-11
        相关资源
        最近更新 更多